SIBO 'C' Software Development Kit OLIB REFERENCE Version 2.30 March 1, 1999 (C) Copyright Psion PLC 1990-98 All rights reserved. This manual and the programs referred to herein are copyrighted works of Psion PLC, London, England. Reproduction in whole or in part, including utilization in machines capable of reproduction or retrieval, without express written permission of Psion PLC, is prohibited. Reverse engineering is also prohibited. The information in this document is subject to change without notice. Psion and the Psion logo are registered trademarks, and Psion, Psion MC, Psion HC, Psion Series 3, Psion Series 3a and Psion Workabout are trademarks of Psion PLC. TopSpeed is a registered trademark of Clarion Software Corporation. Intel 8086 and 80286 are registered trademarks of Intel Corporation. IBM, IBM XT and IBM AT are registered trademarks of International Business Machines Corp. Microsoft and MS-DOS are registered trademarks of Microsoft Corporation. Apple and Macintosh are registered trademarks of Apple Computer Inc. VAX and VMS are registered trademarks of Digital Equipment Corporation. Brief is a registered trademark of Underware Inc. Psion PLC acknowledges that some other names referred to are registered trademarks. CONTENTS 1 Introduction.............sccsccsssscrssersserscsresesceeeeseceseesssesseseseesseesseesseesseessesssessceescesscesscssscesseseseesonees 1-1 sits: OE IB Classesic. 7 scp beet eSegl ocd sdee hand tego eck snug bantstes’ cet ocep sant tact eetouepbantstgecboreeitabetes 1-2 Notation se: cscccteaseasteessdsatcaesiseb cesedsas ceceuahtecsvica secuvdcnbecuvdene cevyachncebavicardcsbidandees dendenvccnneebvess 1-2 INAINIES 20, aac set cacao Ae Seva achvtact dee velit anh Bet daa vautontutuctdentuetomtativedeevaicteatetect ows 1-2 Method function prototypes ...........cccccccccesssceeceseeceeeeeneeeceeeaeeeceeeeeceseaeeeceenaeeeeeeneeeeeeeas 1-2 PRE G:SyMDol is, osc fg l Secs vant te soges Wipeest bt codes sah eiet es odebseep sant hls cesteck Pui eedabeeramae 1-3 LON 8: Parameters so.t5 esse eestesistestediadestedia des tetiadesbtaads vemr hehe tiphaaaeliohatiehee 1-3 Class (las rarniss ses.3 02 c.cs(toeesthet sanvaes cas thet aceesiid ca thbeterevated oes thetoms iiatows eieheaevstnon aes 1-3 Class hierarchy,.2:s.2i0iinsGiien tien cei Ca aktoiee vein cei ia eres 1-3 StructtiredJRrror RECOVELY ..: ccs ccnccregescooepsssicongessbeaupsseteens oduteavpnsbcadsueds bed psdetadessdebspevecesesey 1-4 Use of the p_leave mechanism. ..........e ec eeseesseccsseeeesseecsseecseeecesseecsseecsneeseseeeesaeeesaeers 1-4 Pane NUMBers..e6s ese Stee nt cag eteal Cok etecahectathaabss sche abhadhubk dah aes sO dana hamaseel aa lau etuae es 1-5 2 THE ROOT: Classsiccsscccsctssasessasencooseseesessusacsasssocesosssbsccoessasosoevedscsoasossesouvesssconsadcesasbadescussewassessess 2-1 Class: Geta ti oni yse si5ecenk6 soaps scaceseeibecastevens dekes sncace coungaaaeuaneceewergcenesencconeneseceaseacesnese 2-1 PLOperty *iisessis Asap eicovie basdiiedeh cent hardier daheh badisodartid Avvdis datas Ausiiaieeniat aeons 2-1 ROOT Cth ods acpi: soss7s eas obs shes eat adh coe acens eupiti ed Soa ciien cea dicod dua Ties Seat of ota Ueeh sea te obs dha leee Saas 2-2 Destroy: the:tistan ce 3, s3iissaccsiesaniaiiseeteaesscatlaisacelescaacanesiecealesnah atiseutaatidertaniaseusectegsed 2-2 3 The TIME Cass............scssssssssssssesscsssessscssscsssesssessscsssessscsssesssesssesssessscsssesssesssesssesssessscsssessoess 3-1 PEECUTSOIS ieeet ciyecan cts fovea snneedanetedeuagannpacensvedotngouteccetetedeinboutsceetevedataceussceenetedadscusenesseens 3-2 Class definition .siccccccacecciecoean aeeisatecoedsas (devise te cavices cdavicencdevicae CUevicadieevacae cde vaceadenseces ees 3-2 PLOPOrey, oi oe: Sock in ut Ses Gloss teenken ses abet ccuvaurt Soe alot oan tienes alsvestitratens Giateasther den bivbeaetece ote 3-3 TIME Methods csssisscccssccanccsestanccsecvarcesersas ceseswancenvavancens seancenvacaucebscetndesvacseceasccdedeeecanccaaceae 3-3 DOL TIIME ay. forced fi leaec sc oeasaadu cede edhe odesets locos etic cceas et ce vheniata sd ovaabeuverinvedserionedtesdcavedvaeevedade 3-3 SONSE (UME sevice scsicdestedeiese deeds cedsgiuadcdaeduadedevisabecondsetedevisatcdesdendedeydenbedevccnacdbvvigandevvics 3-4 Add S@CONDS >. 22 ce. ioete cs caeh eee tite dh va sd ae Ate oe dad eee NOs e, aesla es naledonsdacstonnaebeds destiale 3-4 Add: days wicdetsii vid i eiiaedl ea ie ee vee ee a RE 3-5 Ad TOMES ios datedeeete Selec cate leas take Leia cata dena de Soceva dake ctaotes ebivdcavadupede (ecieecetolanedehedey catates 3-5 Ad YEatSs cs.250teoyrive gees tegieee a dayhiduseshees haber oe vQighe ben eae aben ahaa 3-5 SENSETOPMALH seoess cen LEM eet cceses he Neaeeteen oc ROM acerbec de Gack steetenk a Manian ses Guien ent deck 3-6 Set fOrMat. c:.seececdiveeecsgaceacceaa cease vesscccgeseanceaescet cegetsarceavscar ces duancenvicae cons cetneesvecde cdaveuts 3-6 Get system date and time information............ eee eeseeesseeceseeeesseeesseecsseecsseecsseeesseeeesaes 3-7 4 The SGBUF Segmented Buffer Class ................ccsssccsssssscsscssccsscsccssscscscsssssessssssesssssssesssecsees 4-1 PHECUESOLS§. « cceci05 feo2 268 Foss Bec Ais Bek cous Fa bee BeBe ok Bek PEP Me cov Hove tele derevd Feuadeit Pe desovt Paadeeete 4-1 Class etitittion, ic vceseceiters hc catesi toads eteatealesertoateccameauaeasaatecerteateqersaatageusaateoersaateases 4-2 BIOPerty sc. ssh s5 chk Secs Soetes ohccah cies dies co chansne desnugcochaneucausbuacconenehodwuguns cagneceneuevieeacaaenseoeg’ 4-2 SGBUEF methods sec) cicecscestiecsdessciesovsthdessesuseastvst idasduantdasoveticunesctdeassusaddaescesseasseenbinscreacias 4-2 DG StL OY: 205 ies cues cas teibe sel stuns cedeeves dul scunscuvtcuve iva stukeseTsavbes inh steve suv tcuvadas stees eUbsvessebaceve cede aus 4-2 WMI ALISES sxts.terssictec sts Aes tee tec cen ts Agectec certs ee cstds aces tens tes tactsa Bde tds tac sa Ande tiniaeie 42 Sense data by position ...........cececccceessscecessceeeeesnceeeeseeeeceesneeesseneeeeeseeeeeeseaeeeeeeneeeeeeeas 4-3 INSEL tet t taht thea dnctata coi decd td host ected hh tated bale Booed Shin 4-3 OLIB REFERENCE ii Delete iaics cevstensPevsarhsrets tues fevsanasdesstensdeyscedes cadens suv stevs duacdeve deasdevareadevsivensveasevedvosusscvesd 4-3 EXtract: fcsssssccscadseieessasisss adeeseseasshaceandeasooatesiecsotdaaseastetiasevagsaseestegeaes sbdsaseeete shassendaessens 4-4 COMPLESS eoiss ci iesssceeoeisacacoeds cvdgoss Seeauockccgdensd ce gesochdesdeschcashgustegvouubes goavete dedgauh Seesseebsgetaus 4-4 Count Characters ...t..i50.5..sses Aaidasts asses dasstest asses idsseh stewsees Hetsssdavdiee savieseaaedisaceteds 4-4 Sense previous Characters: ,2..5sc2sisects>16; return (p_send4 (self, O_TO_ADD_DAYS,1sw,msw) ) ; } JTO_ADD MONTHS Add months INT to_add_months (INT nmonths) ; Add the signed quantity nmonths to the current date. The current time of day is unaffected. Returns zero if the new date is legal. Otherwise the current date is not changed and the method returns one of the following negative error numbers; E_GEN_UNDER if nmonths is negative and it would take the time earlier than January Ist 1900 E_GEN_OVER if nmonths is positive and it would take the time later than the latest date which can be supported (about 11.7 million years AD). JTO_ADD YEARS Add years INT to_add_years (INT nyears); Add the signed quantity nyears to the current date. The current time of day is unaffected. Returns zero if the new date is legal. Otherwise the current date is not changed and the method returns one of the following negative error numbers; E_GEN_UNDER if nyears is negative and it would take the time earlier than January Ist 1900 E_GEN_OVER if nyears is positive and it would take the time later than the latest date which can be supported (about 11.7 million years AD) OLIB REFERENCE J TO_SENSE_ FORMAT VOID to_sense_format (SE_TIME_FORMAT *pf); Sense format Write the current format data to «pr. See the to_set_format method for an explanation of the fields in the SE_TIME_FORMAT Struct. It is typically used to obtain the current settings before changing a format field by means of the to_set_format method. J TO_SET_FORMAT VOID to_set_format (SE_TIME_FORMAT *pf, UINT mask) ; Set format Stores format parameters which are subsequently used when converting to and from textual representations of time. If pf is NuLL, the method uses system services to set as many components of the format as possible. It does this by sending itself a ro_czET_syspaT message with a format of Ty_TIME_FoRmat. In this case the value of mask is ignored. Otherwise, pf should point to an sz_TIME_FORMAT struct. The values of pf->dsep and pf->tsep should be the new character codes for the required date separator and time separator. Either (or both) may be nu, in which case the corresponding existing separator is not changed. The value of pf->flags, together with mask, sets or clears a combination of format flags. Both should contain an ored combination of the following bit flags, where the bits set in mask determine which items should be changed, and the corresponding bit in pf->£1ags (set or clear) determines the new value. To change all bits as specified by pf->f1ags, mask should be set to oxf+. The bit flags have the following meanings 1n p£->flags: PR_TIME_DDMMYY if present, the date will be written in day-month-year order (European style) PR_TIME_MMDDYY if present, the date will be written in month-day-year order (USA style) PR_TIME_YYMMDD if present, the date will be written in year-month-day order (Japanese style - also good for sorting) PR_TIME_NO_DAY if present, the day is omitted PR_TIME_NO_MONTH if present, the month is omitted PR_TIME_NO_YEAR if present, the year is omitted PR_TIME_MONTH_NAME if present, the month is shown as a name rather than a number PR_TIME_SUFF1IX_NAME if present, a suffix is added to the day number (eg Ist, 2nd, 3rd). PR_TIME_DAY_NAME if set, the day name is written (with a trailing comma) before the date PR_TIME_NO_CENTURY if present, the year is displayed in two digits without the century PR_TIME_NO_SECS if present, the time is displayed without a seconds field PR_TIME_AMPM if present, the time is written in the 12 hour system with a trailing "am" or "pm" (preferred in the USA), otherwise, it is written using the 24 hour system If setting pR_TIME_DDMMYY, PR_TIME_MMDDYY Of PR_TIME_YYMMDD, no more than one of them should be present in pf->flags and all three should be set in mask. (PR_TIME_DDMMyy Is zero so, strictly speaking, it does not need to be set in mask. Setting the other two in mask and not including any of them in pf->flags has the same effect as including pR_timz_ppmmyvy. For this reason, the constant pR_TIME_DATE_ORDER iS defined as a combination of pR_TIME_mMmppyy and PR_TIME_YYMMDD.) 3 THE TIME CLASS The following example first sets default format data from the current system settings. It then modifies the date separator character to a colon (:) and adjusts the time format to include a display of seconds and an am/pm indicator: VOID TimeSetup(PR_TIME *self) { UINT mask; SE_TIME_FORMAT f; p_send4 (self,O_TO_SET_FORMAT,NULL,0); /* set defaults */ f.dsep=':'; f.tsep=NULL; /* don't change this */ £.flags=PR_TIME_AMPM; mask=PR_TIME_NOSECS | PR_TIME_AMPM; p_send4 (self,O_TO_SET_FORMAT, &£,mask); /* clear NOSECS bit and set AMPM bit */ } J TO_GET SYSDAT Get system date and time information INT to_get_sysdat (UBYTE *buf, UINT type, UINT n); Unless type is TY_TIME_FORMAT, Write a time-related name, as a zero terminated string, to *buf and return the length of the string copied. The caller is responsible for ensuring that the buffer is of sufficient size to take the appropriate string. Regardless of the language, each string is guaranteed not to exceed the following lengths: e aday name will not exceed LN_TIME_Day_Name (14) characters e amonth name will not exceed LN_TIME_MoNTH_NaME (14) characters e any format of date string (built from a combination of items, including those written by this method) will not exceed LN_TIME_DATE_sTR (48) characters e any format of time string (built from a combination of items, including those written by this method) will not exceed LN_TIME_TIME_sTR (12) characters The name type is selected by the value of type, as follows: TY_TIME_DAY selects the name of the day corresponding to the day number, n (modulo 7) TY_TIME_MONTH selects the name of the month where n is the month number (modulo 12) TY_TIME_SUFFIX selects the day suffix name where n is the day in month number (modulo 31) TY_TIME_AMPM selects the am/pm string where n is 0 for am and 1 for pm (modulo 2) Although this method may be of use to a client, it is primarily present for internal use when formatting date and time strings. It uses PLIB and operating system services to get the names. A subclass can replace this method if alternative names are required. If type iS Ty_TIME_FoRmaT, the method writes a system-supplied sz_TIME_FoRMAT structure to *buf (with no terminating zero) and returns sizeof (SE_TIME_FORMAT) . The tTy_TIMz_Format type is used by To_sET_rormat when the address of the format data is nuLL. Although this type does not really fit with the others, its inclusion here localises the system-dependent portion of the time class to this method. CHAPTER 4 THE SGBUF SEGMENTED BUFFER CLASS nbytes cur destroy b_init b_point b_insert b_delete b_compress b_ count b_backpoint s s s Ss sb_extract s s Ss s b_allocseg Conceptually, the data held in an instance of the scBur class can be regarded as being stored in a variable sized linear buffer. The data is actually stored in memory in a linked list of allocated heap cells of equal size. Insertions and deletions may allocate or free cells and will, in general, cause data to be transferred from one cell to another. All cells will generally be at least 50% full. Since the segmentation of the data is largely hidden from a user of scBur, the data should not be accessed other than via the supplied methods. Precursors An understanding of the scBur class will be aided by a knowledge of: e the PLIB memory allocator functions. e =the p_enter and p_leave error handling services. OLIB REFERENCE Class definition The scpur class subclasses root and is defined in the sub-category file varray.cl (with generated header file varray.g). CLASS sgbuf root Segmented buffer object { REPLACE destroy ADD sb_init Initialise with segment length ADD sb_point Get the address from a position ADD sb_insert Insert at specified position ADD sb_delete Delete at specified position ADD sb_extract Extract from specified position ADD sb_compress Compress buffer ADD sb_count Return no. of bytes in buffer ADD sb_backpoint Get the address before a position ADD sb_allocseg Allocate memory segments as required TYPES typedef struct { Segment header P_QUE q; Links to neighbouring segments UWORD len; Number of bytes currently in segment } PR_SGBUF_HD; typedef struct { PR_SGBUF_HD *seg; Current segment, or NULL UWORD base; Character position of start of current segment UWORD ofs; Current offset into segment } PR_SGBUF_SBO; } PROPERTY { PR_SGBUF_HD hd; Head of queue UWORD nbytes; Total number of bytes in buffer PR_SGBUF_SBO cur; Current position for efficient positioning } } Property sgbuf.hd The head of the linked list of segments containing the data. Also contains the length of each allocated segment. sgbuf.nbytes The total number of bytes of content. sgbuf.cur The last accessed position as the current segment buffer, the character position of the first character in that buffer and the character offset within the buffer. This is used internally for efficient positioning when scanning sequentially. SGBUF methods J DESTROY Destroy VOID destroy (VOID) ; Delete the content, freeing all of the allocated segments, and then supersend the pEstrRoy message. JSB_INIT Initialise VOID sb_init (UINT len); Initialise the segment queue and set the required length of a segment by setting sgbuf.hd.1len tO len. Note that this is the length to be made available for data. The amount of memory allocated for a segment will actually be sgbuf.hd.1en plus the length of the header (i.e. the length of pR_scBuF_HD). The choice of the value of 1en is a compromise that depends on the nature of the data that is to be stored. 4-2 4 THE SGBUF SEGMENTED BUFFER CLASS A small value reduces the potentially wasted space within each segment (at worst a segment may be only half full) but increases the likelihood that data will need to be moved from one segment to another during insertion or deletion. A small segment size is therefore more appropriate when the data is not expected to change very frequently. A larger value of 1en is more suitable for situations where the data will frequently change, but may result in more potentially wasted space, particularly if the maximum expected content is small. In addition, when data does move from segment to segment, the larger the segment, the more data is likely to be moved. As a rough guideline, it may be noted that all text editing applications on the Series 3 use a segment size of 64 bytes. If necessary in a particular case, an optimum value can be found empirically by timing a typical operation with a range of different segment sizes. When a sequence of fixed length items is to be stored, insertion, deletion and access to items will be much more efficient if the segment size is an exact multiple of the item size. No segments are allocated until data is inserted. J SB POINT Sense data by position UINT sb_point (VOID **pbuf, UINT pos); Write, to *pbuf, the address of the data at position pos. Calls p_panic (P_PANIC_P_SGBUF_1) If pos is outside the range of the segmented buffer content. Updates its internal record of the current position. Returns the number of contiguous bytes of data available at *pbut. Does not write to *pbuf and returns zero if there is no data in the segmented buffer. SB_INSERT Insert VOID sb_insert (UINT pos, VOID *pbuf, UINT len) Create a gap of size 1en bytes at position pos in the segmented buffer and then inserts the 1en bytes of data from pbuf. The inserted data may span more than one segment, with more segments being allocated as necessary. If opening the gap causes data to overflow from the segment containing the insertion point, then this data will be inserted into the last of any newly created segments and/or any immediately following segment that previously existed. The allocation of segments is performed by sending an sB_ALLOCSEG message. The value of sgbuf.nbytes is adjusted to indicate the new number of bytes in the segmented buffer. Calls p_panic (P_PANIC_P_SGBUF_1) if position pos is outside the range of the segmented buffer. Inserts nothing and calls p_leave (E_GEN_NoMEMoRY) if it fails to allocate any extra segments required. JSB_ DELETE Delete VOID sb_delete(UINT pos, UINT len); Delete 1en bytes from position pos. This may involve copying data between segments. If a segment becomes empty then it will be freed. The value of sgbuf.nbytes is adjusted to indicate the new number of bytes in the segmented buffer. Calls p_panic(P_PANIC_P_SGBUF_1) if position pos is outside the range of the segmented buffer. Calls p_panic (P_PANIC_P_SGBUF_2) if position pos+1len 1s outside the range of the segmented buffer. OLIB REFERENCE JSB_EXTRACT Exract VOID sb_extract (UINT pos, VOID *buf, UINT len); Copy len bytes of data from position pos into the buffer at bur. If necessary, data is copied from more than one segment. The buffer is assumed to be large enough to hold 1en bytes of data. Calls p_panic (P_PANIC_P_SGBUF_1) if position pos is outside the range of the segmented buffer. J SB COMPRESS Compress VOID sb_compress (VOID) ; Compress the segmented buffer by moving data to fill the early segments. Any segments that are emptied by this process will be freed. J SB COUNT Count characters UINT sb_count (VOID) ; Return the number of bytes currently held within the segmented buffer. J SB _BACKPOINT Sense previous characters UINT sb_backpoint (VOID **pbuf, UINT pos); Write, to *pbuf, the address of the byte of data that is the lowest in memory and contiguous with the byte at position pos-1. In general, *pbuf will contain the address of the first byte of data in the segment containing the byte at position pos. If position pos is at the beginning of a data segment, *pbuf contains the address of the first byte of data in the previous data segment. Returns the number of bytes between the position corresponding to *pbuf and position pos. If the return value is n, data is only guaranteed to be valid at addresses of *pbuf to *pbuf+n-1 inclusive. If pos is zero nothing is written to *pbuf and the method returns zero. This is the only position for which the return value is zero. Calls p_panic(P_PANIC_P_SGBUF_1) if position pos is outside the range of the segmented buffer. This method may be regarded as the complement of sb_point and would typically be used when searching backwards through the content. SB_ALLOCSEG Allocate segments INT sb_allocseg(PR_SGBUF_HD *pseg, UINT nseg); Allocate a chain of nseg empty segments and insert the chain after the segment at *pseg. Each allocated segment is initialised to be empty by setting the 1en field of the segment header to zero. The amount of memory required for each segment is sgbuf.hd.1len plus the length of the segment header (i.e. the length of PR_SGBUF_HD). This method is used internally and is not intended to be called by a user of the scBur class. Does not allocate any segments and calls p_1leave (E_GEN_NOMEMoRY) if there is not enough memory to allocate all nseg segments. CHAPTER 5 VARIABLE ARRAY CLASSES The classes described in this chapter implement arrays of a variable number of records, in which records are referenced by number. The first record is record zero and the last record is record n-1, where n is the total number of records in the array. Depending on the particular class, records may be of fixed or variable length. Unlike static C arrays, the space for the array is dynamically allocated from the heap. This means that adding a record to an array can fail owing to a failure to allocate additional memory. For some subclasses, it is possible to pre-set the capacity; this facility may be used when the required capacity is known in advance in order to avoid out of memory failures. Extending the capacity of an array generally involves either allocating additional heap cells or growing a heap cell using p_realloc. This is always done in such a way that the original handle to the object does not change. Precursors An understanding of the variable array classes will be aided by a knowledge of: e the PLIB memory allocator functions e =the p_enter and p_leave error handling services Class diagram om ae i varoot / Ban Z vaflat / Z sgbuf / ~ ns) oe + ) L as 20 aes ¢ vaxvars / re - ) Se tees eae ¢ vaxvar / = ) Lae OLIB REFERENCE Usage summary varootT and vaFrrx are abstract classes. varoot defines a relatively large number of deferred methods in order to promote polymorphism between the directly usable classes. The vastr class is used to create arrays of variable length text records, stored as zero terminated strings. The storage overhead per string is only one byte, so it is particularly suitable for storing short strings, of up to, say, several tens of bytes, or for strings with a wide variation in size (such as file names, which can be any length up to 128 bytes, but are usually much shorter). Since the whole array is stored in a single allocated cell, the vastr class is most suitable for arrays that contain: e asmall number of records ¢ amoderately large, but fixed maximum, number of records (for which the maximum capacity can be allocated in advance) Because of its suitability for storing file names, the vastr class is used, for example, to store directory listings. In general, however, the vastr class is not particularly suitable for arrays which can dynamically grow to a very large size. The resulting repeated calls to p_realloc are likely to cause heap fragmentation and seriously reduce the effective use of memory. The vartat class is used to create arrays of fixed length records, where the whole array is stored in a single allocated cell. The preferred usage is as for the vastr class. The vassc class is used to create arrays of fixed length records where the array is segmented into a number of equal sized blocks. It is suitable for large, dynamically changing arrays. Its disadvantage is that it takes longer to locate a random record by record number because it has to count through the segments, although sequential access to the records is reasonably efficient. The vaxvar class is used to create arrays of variable length records which, unlike those of the vastr class, may contain arbitrary data. It uses an index which is stored in a single allocated cell and thus, like vastR and vaFr.art, 1s best suited to arrays containing either a small number of records or a larger but fixed number of records. Since each record is stored in a separate allocated cell, it is more suited to the storage of longer records, where the increased overhead per record is less significant. The vaxvars class (which has a segmented index) should be used instead of vaxvar when there is a possibility of growth to a large number of records. Record pointers Many of the variable array methods take a record pointer prec as a parameter. The va_prec method, defined as a deferred method by varoot, and which converts a record number into a prec record pointer, assumes that each record is stored in such a way that it is possible to provide a record pointer that is equivalent to an external pointer. In most subclasses this assumption is valid. Subclasses that are, because of their internal structure, unable to provide a va_prec method may still inherit usefully from varoot, but should replace all inherited methods which rely on va_prec (va_findisq, va_search and va_compare). In most, but not all, subclasses of varooT, prec is the address of the record data. A more general interpretation of prec is that it is a handle to a record in the array. In a variable length record array subclass, for example, prec might be the address of a string descriptor which contains the address and length of a buffer containing the record. In contrast with va_prec, the va_pbuf method converts a record number into a pointer that is guaranteed to point to the record data. Although in most cases (see, for example, varLat and vastTR) va_prec and va_pbuf return identical pointers, they would return different addresses in the case mentioned in the previous paragraph (see also the vaxvar and vaxvars classes). In general, there is no guarantee that any method will not cause the data of one or more records to move in memory. An application should therefore always access records by record number and should not store the pointers supplied by either the va_prec or the va_pbuf method. When scanning records, the user should not rely on the assumption of contiguous storage to scan through the records. Although that is true for some types of variable array, it is certainly not true in general. None of the supplied methods ever refers to more than two record pointers at any one time. This means that the data of a variable array may be stored in a separate segment and only the last two accessed records need to be copied into the local data space. 5 VARIABLE ARRAY CLASSES VAROOT VAROOT destroy va_count va_delete va_sort va_key va_findisg va_insertisq va_append va_insert va_search va_compare va_reset va_test va_replace va_copy va_reclen va_swap va_init va_insertm va_prec va_pbuf va_capacity va_compress va_deletem The varoot class is an abstract class which must be subclassed to provide a usable variable array class. This abstract class does not assume that the records are of fixed length. It is designed so that it may be subclassed by classes in which the records are of either fixed or variable length. The assumption that prec is a pointer to the record itself is made by only one method in this class - va_test. If this assumption is not true for a particular subclass, that subclass should replace va_test with a more appropriate method. Class definition Defined in sub-category file varray.cl (generated header file varray.g). CLASS varoot root The root class for variable arrays. { REPLACE destroy prppprrrrrrere GDUUOTGTV00 00D DEFER DEFER DEFER DEFER DEFER DEFER DEFER DEFER DEFER DEFER DD va_count D va_delete D va_sort D va_key D va_findisq D va_insertisgq D va_append D va_insert D va_search D va_compare D va_reset D va_test DD va_replace va_copy va_reclen va_swap va_init va_deletem va_insertm va_prec va_pbuf va_capacity va_compress CONSTANTS { VA_ROOT_DUPLICATE VA_ROOT_FLG_FOLD VA_ROOT_FLG_DESC i Send itself a va_reset message first Return record count Delete a record Sort array Set key parameters Find a record in an ordered array Insert a record in sequence Append after last record Insert before a record Search for a match Compare two records Reset to zero records and capacity Compare a record with a test record Replace a record Copy a record Return record length Swap two records Initialise an array Delete a record range Insert a record sequence Return the address of record n Point at the buffer data - usually same as prec Set record capacity Compress memory usage 1 Ox01 0x02 OLIB REFERENCE TYPES { typedef struct { UBYTE ofs; offset for comparison UBYTE len; length for bcmp (scmp if zero) UBYTE fold; fold case if set UBYTE desc; reverse compare result if set } PR_VAROOT_KEY; } PROPERTY { UWORD nrec; number of records in the array PR_VAROOT_KEY key; defines key for sort etc } } Property varoot.nrec Holds the current record count. Each subclass is expected to maintain this field. varoot .key Holds the current sort key. See the va_key method for a description of the PR_VAROOT_KEYy Structure fields. VAROOT methods & DESTROY Destroy VOID destroy (VOID) Destroy the array by sending itself a va_RESET message before supersending a DESTROY message. & VA_COUNT Count records UINT va_count (VOID) Return the number of records in the array from varoot .nrec. VA_APPEND Append a record VOID va_append(VOID *prec) Append the record pointed to by prec to the end of the array. This is done by sending itself a va_INSERT message to insert the record at a position given by varoot.nrec. It will call p_1eave if the va_insert method for that particular subclass calls p_leave, for example, if it fails to allocate any necessary additional memory. VA_INSERT Insert a record VOID va_insert (UINT recno, VOID *prec) ; Insert the record pointed to by prec before record recno. This is done by sending itself a va_INSERTM message with recno, prec and 1 (i.e. one record) as parameters. It will call p_teave if the va_insertm method for that particular subclass calls p_1eave, for example, if it fails to allocate any necessary additional memory. 5 VARIABLE ARRAY CLASSES & VA_DELETE Delete a record VOID va_delete(UINT recno); Delete record recno from the array by sending itself a va_DELETEM message with recno and 1 (i.e. one record) as parameters. & VA_KEY Set key for comparisons VOID va_key(UINT offset, UINT length, UINT flags); Define the parameters which are used by va_test and, indirectly, by va_compare, va_sort, va_findisq and va_insertisq. The parameters are: offset - sets the offset (0 to 127 inclusive) into the record at which the comparison begins. The value is copied into varoot.key.ofs. length - if non-zero, this sets the length of the comparison (1 to 127 inclusive) and, if zero, sets the comparison to be between two zero terminated strings. The value is copied into varoot.key.len. flags - any combination of the two flags va_RooT_FLG_FOLD and va_RooT_FLG_DEsc. If the VA_ROOT_FLG_FOLD flag is set, the comparison is case independent. If the va_Root_FLG_pEsc flag is set, the result of the comparison is reversed, to give descending rather than ascending order. The values of flags&VA_ROOT_FLG_FOLD and flags&VA_ROOT_FLG_DESC are copied into varoot .key.fold and varoot .key.desc respectively. The default settings are offset = 0, length = 0, flags = 0, giving ascending order, case-dependent string comparisons, from offset zero in the record buffer. See the va_test method for further discussion of the va_key parameters. & VA_TEST Compare two records by pointer INT va_test (VOID *precl, VOID *prec2); Compare the record pointed to by prec2 with the record at preci on the assumption that the records contain text. The basis for the comparison is subject to the contents of varoot .key, as set by the method va_key, aS follows: if varoot .key.len==0 it uses p_scmp df varoot.key.fold is FALSE), or p_scmpi af varoot.key.fold is TRUE). if varoot .key.len>0 it uses p_bemp (if varoot .key. fold iS FALSE), OF p_bcempi af varoot.key.fold is TRUE). The default is to use p_scmp. Returns the logical equivalent of (*preci-*prec2) - that is, zero if the two records are equal, negative if *prec1 is before (less than) *prec2, positive if after. If key. desc is TRUE this result is reversed. This method is used directly by va_findisg, va_search and va_compare. It is used indirectly by va_sort and va_insertisq. Subclasses which are unable to provide va_prec, or in which va_prec does not return a pointer to the data to be compared, must replace this method. & VA_COMPARE Compare two records by number INT va_compare(UINT nl, UINT n2); Compare record ni with record n2 by sending itself va_pREc messages to get pointers to the records and then sending itself a va_tEst message to perform the comparison that determines the return value. Returns the logical equivalent of n1-n2, that is, zero if the two records are equal, a negative value if record n1 is less than (before) record n2 or a positive value if record ni is greater than (after) record n2. Note the effect of the va_RooT_FLG_FOLD and va_ROoT_FLG_pDEsc flags. This method is used directly by va_sort. Subclasses which are unable to provide va_prec must replace this method. OLIB REFERENCE VA_SORT Sort VOID va_sort (VOID) Sort the records of the array. The sort uses the quicksort algorithm. This is an efficient exchange sort using va_compare and the deferred va_swap respectively to compare and to exchange two records. Subclasses which are unable to implement va_swap efficiently should either not support va_swap or subclass va_sort to sort by some other means. Not supporting va_swap does not mean that the array may never be ordered; ordered arrays can still be constructed using va_insertisq. The method uses va_compare (and hence va_test) to compare two records. The sort key is determined by va_test, as qualified by the last use of va_key. If the flexibility afforded by va_key is insufficient (for example, to sort on multiple keys) va_test may be replaced. Note that the vastr class does not support this method. In such a case the array may be built in order, by using the va_insertisq method. & VA_FINDISQ Find (binary chop) INT va_findisq(VOID *pkey, VOID *pmid) ; Find a record in an ordered array, using the binary search algorithm. The record to be found is specified by pkey which is typically a record pointer (prec). Returns zero if an exact record match is found, with the record number of the matching record in *pmia. If there is no matching record, *pmid contains the record number of one of the two records adjacent to the key and va_findisg returns the logical equivalent of *pkey-*pmid, that is, a negative value if *pkey is less than (before) the existing record with record number *pmia, or a positive value if *pkey is greater than (after) record number *pmid. The method uses va_test, passing pkey as the first parameter; the second is a pointer to one of the records in the ordered variable array and is determined by the binary search algorithm itself as it works through its search. The result will be unpredictable if the array is not ordered. (An array may be ordered either by applying va_sort or by using va_insertisgq to build the array.) VA_INSERTISQ Insert in sequence INT va_insertisq(VOID *prec,UWORD *precno) ; Insert the record pointed to by prec in sequence into an ordered array using va_findisg to locate the insertion point. If there is no matching record, it sends itself a va_INnsERTm message. If the insertion is successful, the record number of the inserted record is written to *precno and va_insertisq returns zero. The record is not inserted if there is already a matching record in the array (that is, if va_compare would return zero). In this case va_insertisq returms VA_ROOT_DUPLICATE (which is a positive number) and writes the matching record number to *precno. Since it sends a vA_INSERTM message, it is quite possible for the insert to fail with out of memory and call p_leave. © VA_SEARCH Search INT va_search(VOID *pkey) ; Sequentially search for a record which exactly matches that specified by pkey. The search is performed by sending a va_tEsT message to compare each record in the array (obtained by use Of va_prec) with the data pointed to by pkey, which is assumed to be a record pointer. Returns the record number if found (+ve number or zero) or E_GEN_FAIL if not. 5-6 5 VARIABLE ARRAY CLASSES & VA_RESET Reset VOID va_reset (VOID) Reset the array to its state just after its creation (and, if appropriate, a va_init). Following a va_reset the array contains no records and has no record capacity. The method is implemented by sending itself a va_bzELETEM message to delete varoot .nrec records, starting from record 0. This is followed by a va_compress message to discard all record capacity. The method has two principal uses: e It provides the client with a concise and efficient way to delete all records in the array and, at the same time, to zero the capacity. e itis used by the destroy method to remove all allocated cells, other than the object instance cell, prior to the freeing of the instance itself. This implementation assumes that the va_compress method always frees all additional allocated cells when an array contains no records. (This assumption is true for all OLIB array classes.) VA_REPLACE Replace a record VOID va_replace(UINT recno, VOID *prec); Replace record number recno with the record pointed to by prec. The method is implemented by using a va_DELETE message to delete record recno, followed by a VA_INSERT message to insert prec at position recno. The implementation of this method is aimed at variable length record subclasses; fixed length record subclasses can replace it by a more efficient method (for example, by simply overwriting the record data). Deferred VAROOT methods & VA_COPY Copy a record UINT va_copy(UINT recno, VOID *prec); A deferred method for copying the contents of record recno tO prec. Returns the length copied. Not used by any methods in this class but deferred, to promote polymorphic subclasses. & VA_RECLEN Get record length UINT va_reclen(UINT recno); A deferred method for returning the record length of record recno. Not used by any methods in this class but deferred, to promote polymorphic subclasses. VA_SWAP Swap two records VOID va_swap(UINT nl, UINT n2); A deferred method for swapping the contents of record n1 with those of record n2. Used by va_sort. OLIB REFERENCE VA_INIT Initialise VOID va_init(...) A deferred method for initialising the property of the newly created array. No record capacity is allocated until the first record insertion or va_capacity message. All subclasses should ensure that this is the case. However, the method may still fail, calling p_1eave (&_GEN_NOMEMORY) if the subclass has other memory requirements (vasec, for example, creates a component object). The parameters depend upon the subclass. For example, a fixed length record subclass would require the record length as a parameter. Not used by any methods in this class but deferred, to promote polymorphic subclasses. VA_CAPACITY Set capacity VOID va_capacity(UINT nspc); A deferred method for setting the capacity of the internal storage. The interpretation of the parameter nspc is dependent on the subclass. For example, nspc might reasonably be the number of records in a fixed length record subclass, or it might be the total number of bytes of allocated storage in a variable length record subclass. It is not always possible to provide this method in a meaningful way and some subclasses are expected to dummy it. Not used by any methods in this class but deferred, to promote polymorphic subclasses. VA_COMPRESS Compress VOID va_compress (VOID) ; A deferred method for compressing the capacity of the array as much as is reasonable and sensible (this judgement is left to the subclass). The supplied destroy method assumes that va_compress frees all allocated cells used to hold records when there are no records in the array. If this is not true, the destroy method must be subclassed. The va_compress method is used directly by va_reset and indirectly by destroy. VA_DELETEM Delete sequence of records VOID va_deletem(UINT recno, UINT nrecs); A deferred method for deleting nrecs records, starting with record number recno. Used directly by va_delete and va_reset and indirectly by destroy. VA_INSERTM Insert sequence of records VOID va_insertm(UINT recno, VOID *prec, UINT nrecs) ; A deferred method for inserting a sequence of nrecs records, pointed to by prec, before record recno. If it fails to allocate enough memory to hold the new records it should allocate nothing and call p_leave since, typically, va_insert and va_append are void functions and va_insertisq returns an insertion indicator. Used directly by va_insert and indirectly by va_append and va_insertisq. 5 VARIABLE ARRAY CLASSES VA_PREC Point to record VOID *va_prec(UINT recno); A deferred method for returning a pointer to record recno. See the introductory discussion of the varoot class for the meaning of a record pointer. Used directly by va_compare, va_findisg and va_search, and indirectly by va_insertisq and va_sort. VA_PBUF Point to record data VOID *va_pbuf (UINT recno); A deferred method for returning a pointer to the record data of record recno. Typically, it returns the same value as for va_prec. in some classes, however, the record pointer may not be the same as the record data pointer (this is true for the vaxvar and vaxvars classes) or the record may contain a header to the data. The va_pbuf method should always be used in preference to va_prec when a pointer to the data of the record is required. VAFIX VAROOT destroy va_replace va_count va_copy va_delete va_reclen va_sort va_swap va_key va_findisgq va_init va_insertisq va_deletem va_append va_insertm va_insert va_prec va_search va_pbuf va_compare va_capacity va_reset va_compress va_test The varrx class is an abstract class for fixed length record variable arrays. In addition to adding the record length rien to the property, it: e implements the deferred methods va_swap, va_copy and va_reclen. e replaces va_replace by a more efficient method. The methods are provided on the assumption that the record pointer prec is simply the address of the record (reasonable when records are of fixed length). OLIB REFERENCE Class definition Defined in sub-category file varray.cl (generated header file varray.g). CLASS vafix varoot Fixed length record variable arrays. { REPLACE va_replace More efficient than varoot's REPLACE va_swap Uses p_bswap REPLACE va_copy Uses p_bcpy REPLACE va_reclen Returns property value PROPERTY { UWORD rlen; Record length } } Property vafix.rlen the record length, set by a subclass va_init method. Each subclass is expected to set this field. VAFIX methods & VA_REPLACE Replace a record VOID va_replace(UINT recno, VOID *prec); Replace the specified record by sending itself a va_pREc message to convert recno into a record pointer and then using p_bcpy to overwrite that record with the record at prec. & VA_COPY Copy a record UINT va_copy(UINT recno, VOID *prec); Copy the specified record to prec by sending itself a va_pREc message to convert recno into a record pointer and then using p_bcpy to copy that record data from the array to prec. Returns the length copied. & VA_RECLEN Get record length UINT va_reclen (VOID) Return the record length, that is, the value of the vafix.rlen property field. & VA_SWAP Swap two records VOID va_swap (UINT n1,UINT n2) Swap the contents of record n1 with record n2 by sending two va_prEc messages to itself to turn n1 and n2 into record pointers and then using p_bswap to swap the record contents. 5 VARIABLE ARRAY CLASSES VASTR VAROOT key destroy va_replace va_copy va_count va_reclen va_delete va_init va_sort va_deletem va_key va_insertm va_findisgq Ad va_prec va_insertisq va_pbuf va_append i va_capacity va_insert va_compress va_search va_compare va_reset va_test The vastr class may be used to create arrays of variable length text records which are stored as zero terminated strings. Non-textual data may be used provided that the record data does not have any zero bytes in it. The whole array is stored in a single allocated cell. When a record is inserted into a full array, the single cell is reallocated to accommodate the additional record. Deletions do not automatically reduce the record capacity, but the capacity may be reduced manually using va_compress Of va_capacity. The record pointer prec points to a zero terminated string. Internally, the records are stored as a contiguous sequence of zero terminated strings. To locate a random record by record number, va_prec has to count through the records. However, the object remembers the last record accessed so that scanning the array sequentially from the first record is reasonably efficient. The vastr class is suitable for short arrays or for large arrays which have a known maximum capacity (in terms of the number of bytes required). It is not suitable for arrays which can dynamically grow to a large size because heap fragmentation can seriously reduce the effective use of the heap. The string array is especially suitable for strings that can vary greatly in size since the overhead per string (1 byte) is small. A typical use is for holding file name lists where each string can have a maximum length of p_rwames1zE (128) bytes but is usually less than 32 bytes long. OLIB REFERENCE Class definition Defined in sub-category file varray.cl (generated header file varray.g). CLASS vastr varoot Variable length text record variable arrays REPLACE va_copy REPLACE va_reclen REPLACE va_init REPLACE va_compress REPLACE va_capacity REPLACE va_deletem REPLACE va_insertm REPLACE va_prec REPLACE va_pbuf=vastr_va_prec PROPERTY { UWORD size; current size of the array in bytes UWORD gran; re-alloc granularity UBYTE *base; base of the array UWORD len; offset to end of used data UWORD num; number of last record referenced UBYTE *pnum; pointer to record num } } Property vastr.size The current size of the allocated cell that contains the data. It should not be accessed by any subclass. vastr.gran The granularity, in bytes, used when expanding the allocated cell. It vastr.base vastr. vastr. vastr.pnum len num should not be accessed by any subclass. The allocated cell base. It should not be accessed by any subclass. The byte offset from vastr.base to the end of the data in the allocated cell. It should not be accessed by any subclass. The record number of last record referenced. It should not be accessed by any subclass. A pointer to the record specified by vastr.num. It should not be accessed by any subclass. VASTR methods © VA_INIT Initialise VOID va_init (UINT granularity) Initialise the array by setting vastr.gran to the passed granularity, which must be at least as large as the longest record to be inserted. No capacity is actually allocated until the first insertion. The granularity is significant when an insertion requires an increase in capacity; the increase is such that the capacity, in bytes, is made an exact multiple of the granularity. Making this value larger means that the array cell needs to be reallocated less frequently as a result of insertions, but more memory may be wasted in unused capacity. Any unused capacity may be recovered by sending a va_comPpRESS message when the building of an array is complete. 5 VARIABLE ARRAY CLASSES VA_CAPACITY Set capacity VOID va_capacity(UINT nspc); Set the capacity of the allocated array cell by reallocating it to have exactly the capacity for nspc bytes (the granularity has no effect). The minimum space required is equal to the sum of the string lengths of all the records plus one byte per record for the terminating zero. Calls p_panic (P_PANIC_P_VASTR_2) If nspc is less than the current size of the array. & VA_COMPRESS Compress VOID va_compress (VOID) Compress the capacity of the array to that which will exactly contain the current records by sending itself a VA_CAPACITY message with vastr.len as the size. If there are no records in the array, the array cell is freed (as required by varoorT). & VA_DELETEM Delete a sequence of records VOID va_deletem(UINT num, UINT nrecs) ; Delete the sequence of nrecs records, starting at record number nun, and decrease the value of varoot.nrec by nrecs. The deletion is performed by simply copying all following records over the records to be deleted. No memory is freed. Calls p_panic(P_PANIC_P_VASTR_4) if num+nrecs 1s greater than the number of records in the array. VA_INSERTM Insert a sequence of records VOID va_insertm(UINT num, VOID *prec, UINT nrecs); Insert the sequence of nrecs records, pointed to by prec, before record number nun, and increase the value of varoot .nrec by nrecs. The valid range for num is from zero to the number of records inclusive. In the record sequence at prec, each subsequent string should immediately follow the zero terminator of the previous string. Calls p_panic(P_PANIC_P_VASTR_3) if num is greater than the number of records in the array. Inserts no records and calls p_leave (E_GEN_NOMEMoRY) if there is not enough memory available to hold all the additional records. & VA_RECLEN Get record length UINT va_reclen(UINT num); Return the record length of record number num. The returned length excludes the zero terminator. & VA_PREC Point to record VOID *va_prec(UINT num); Return the address of record number num. The pointer returned is to a zero terminated string. Calls p_panic(P_PANIC_P_VASTR_1) if num is greater than or equal to the number of records in the array. OLIB REFERENCE & VA_PBUF Point to record data VOID *va_pbuf (UINT num) AS VA_PREC. & VA_COPY Copy a record UINT va_copy(UINT num, VOID *prec); Copy the record specified by the parameter num to the location pointed to by the parameter prec; by sending itself a va_PREC message to convert recno into a record pointer and then using p_bcpy to copy that record data from the array to prec. Returns the length copied. VAFLAT VAROOT VAFIX destroy va_replace va_init va_count va_copy va_compress va_delete va_reclen va_deletem va_sort va_swap va_insertm va_key va_capacity va_findisgq ind va_prec va_insertisq va_pbuf va_append va_insert va_search va_compare va_reset va_test The variat class may be used to create arrays of fixed length records where the whole array is stored in a single allocated cell. When a record is inserted into a full array, the single cell is reallocated to accommodate the additional record. Deletions do not automatically reduce the record capacity but the capacity may be reduced manually using va_compress Or va_capacity. The vartat class is suitable for short arrays or for large arrays which have a known maximum capacity. It is not suitable for arrays which can dynamically grow to a large size because heap fragmentation can seriously reduce the effective use of heap memory. Class definition Defined in sub-category file varray.cl (generated header file varray.g). CLASS vaflat vafix Flat allocated (in a single cell) fixed length variable arrays { REPLACE va_init REPLACE va_compress REPLACE va_deletem REPLACE va_insertm REPLACE va_capacity REPLACE va_prec REPLACE va_pbuf=vaflat_va_prec PROPERTY { UWORD gran; granularity in records UWORD nspc; present record capacity UBYTE *base; start of variable array } 5 VARIABLE ARRAY CLASSES Property vaflat.gran The granularity, in records, in which to allocate memory. It should not be accessed by any subclass. vaflat.nspe The number of fixed sized record slots allocated (greater than or equal to the number of records in the array). It should not be accessed by any subclass. vaflat.base The allocated space handle, i.e. the start of the variable array. It should not be accessed by any subclass. VAFLAT methods © VA_INIT Initialise VOID va_init (UINT reclen, UINT gran); Initialise the array by setting vafix.rlen from reclen and vaflat.gran from gran. No capacity is actually allocated until the first insertion, hence no errors can occur. The granularity is significant when an insertion requires an increase in capacity; the increase is such that the capacity, in records, is made an exact multiple of the granularity. Increasing the value of gran means that the array cell needs to be reallocated less frequently (hence less heap fragmentation) as a result of insertions, but more memory may be wasted in unused capacity. Any unused capacity may be recovered by sending a vA_compREss message when the building of an array is complete. VA_CAPACITY Set capacity VOID va_capacity(UINT nspc); Set the capacity of the array cell by reallocating it to have exactly the capacity for nspc records (i.e. nspc*vafix.rlen bytes). Does not alter the capacity and calls p_leave (E_GEN_NoMEMoRY) if there is not enough memory for the specified capacity. Calls p_panic (P_PANIC_P_VAFLAT_3) if nspc is less than the current number of records in the array. & VA_COMPRESS Compress VOID va_compress (VOID) Compress the capacity of the array to exactly that required to hold the current records. This is achieved by sending itself a va_capaciTy message to set the capacity to varoot .nrec records. If there are no records in the array, the array cell is freed as required by the varoort class. & VA_DELETEM Delete sequence of records VOID va_deletem(UINT recno, UINT nrecs); Delete the sequence of nrecs records starting at record recno. Calls p_panic (P_PANIC_P_VAFLAT_4) if recnotnrecs is greater than the number of records in the array. The number of records in the array, held in varoot .nrec, 1s reduced by nrecs. The delete is performed by copying the data of following records over the records being deleted. No allocated space is freed. VA_INSERTM Insert sequence of records VOID va_insertm(UINT recno, VOID *prec, UINT nrecs) Inserts the sequence of nrecs records pointed to by prec before record recno where recno is between zero and the number of records inclusive. OLIB REFERENCE If the array cannot currently hold all the additional records, the record capacity is set (by sending itself a VA_CAPACITY message) to the smallest exact multiple of the granularity that is greater than the total number of records to be held. The data is inserted by opening up a gap in the array (by buffer copying) then copying the new data into the gap. Inserts no records and calls p_1eave (E_GEN_NOMEMOoRY) If there is not enough memory available to hold the additional records. Calls p_panic(P_PANIC_P_VAFLAT_2) if recno is greater than the number of records in the array. & VA_PREC Point to record VOID *va_prec(UINT recno) Return the address of record recno. Calls p_panic(P_PANIC_P_VAFLAT_1) if recno is greater than or equal to the number of records in the array. & VA_PBUF Point to record data VOID *va_pbuf (UINT recno); AS VA_PREC. VASEG VAROOT VAFIX ae eet destroy va_replace va_init va_count va_copy va_compress va_delete va_reclen va_deletem va_sort va_swap va_insertm va_key va_capacity va_findisgq Het va_prec va_insertisq va_pbuf va_append va_insert va_search va_compare va_reset va_test The vassc class may be used to create arrays of fixed length records where the array is segmented into a number of equal sized blocks. The segments are allocated from the heap and are doubly linked. The segmented array object is suitable for large dynamically changing arrays and is substantially more likely to make efficient use of the available heap memory. Its disadvantage is that it takes longer to locate a random record by record number because it has to count through the segments. However, the object remembers the last record accessed so that scanning a segmented array sequentially from the first record is reasonably efficient. The segments are generally not less than 50% full. See the scpur class documentation (in the SGBUF Segmented Buffer Class chapter) for more information on the segmentation mechanisms. 5 VARIABLE ARRAY CLASSES Class definition Defined in sub-category file varray.cl (generated header file varray.g). CLASS vaseg vafix Segmented fixed length variable arrays { REPLACE va_init REPLACE va_compress REPLACE va_capacity=p_dummy REPLACE va_deletem REPLACE va_insertm REPLACE va_prec REPLACE va_pbuf=vaseg_va_prec PROPERTY 1 { PR_SGBUF *buf; segmented buffer } } Property vaseg.buf The object handle of the owned instance of the segmented buffer (scpur) class. This may be used by any subclass to access the scBur object. VASEG methods VA_INIT Initialise VOID va_init (UINT rlen, UINT granularity); Initialise the array by setting the record length to rien, creating a scBur object and initialising it with a segment size of rlen*granularity. Note that, since the segment size is an exact multiple of the record length, the content of a record will never straddle a segment boundary (significant for the va_prec and va_pbuf methods). The first segment is not allocated until the first insertion. Calls p_leave (E_GEN_NoMEMoRyY) if it cannot create the scpur object. VA_CAPACITY Set capacity Setting the capacity does not make any sense for the vaseg class and this method does nothing. & VA_COMPRESS Compress VOID va_compress (VOID) Compress the capacity of the array by sending an sB_comrEss message to the owned scBur object. & VA_DELETEM Delete a sequence of records VOID va_deletem(UINT recno, UINT nrecs); Delete the sequence of nrecs records starting at record recno. The delete is performed by sending an SB_DELETE message to the owned scBur object. Reduces the record count by nrecs. OLIB REFERENCE VA_INSERTM VOID va_insertm(UINT recno, VOID *prec, UINT nrecs) ; Insert a sequence of records Insert the sequence of nrecs records pointed to by prec before record recno, where recno is between zero and the number of records inclusive. The insert is performed by sending an sB_INsERT message to the owned scBur object. Increases the record count by nrecs if the insertion was successful. Inserts no records and calls p_leave (E_GEN_NOMEMoRY) if there is not enough memory available to hold the additional records. & VA_PREC Point to record VOID *va_prec(UINT recno) Returns the address of record number recno. The record pointer is obtained by sending an sp_pornt message to the owned scBurF object. Calls p_panic(P_PANIC_P_VASEG_1) if recno is greater than or equal to the number of records in the array. & VA_PBUF Point to record data VOID *va_pbuf (UINT recno) AS va_prec. VAXVAR VAROOT VAFIX VAFLAT gran nspc destroy va_count va_delete va_sort va_key va_findisgq va_insertisq va_append va_insert va_search va_compare va_reset va_test va_compress ac4 tem vFarinsertm va_capacity va_prec vwarpbut va_test va_copy va_reclen va_replace va_init va_deletem va_insertm va_pbuf The vaxvar class may be used to create arrays of variable length records where there is no restriction on the values of the bytes which can be stored within records. Each record is stored in its own heap cell and the records are indexed by a variat array of Rc_vAxvaR structs. The vaxvar class subclasses the fixed length record array vartat which provides its index. Although vaxvar has variable length records, only 8 of the methods inherited from variat needed to be replaced. In vaxvar, records are not described simply by their address but indirectly via the address of a record descriptor. A record descriptor is a RC_vaxvar struct that contains the address and length of a record. The rc_vaxvar struct is used to specify records both outside and inside the array. For example, the insertion methods va_append, va_insert, va_insertisg and va_insertm all require the address of an RC_VAXVAR Struct - and the va_prec method returns the address of an rc_vaxvar struct. 5 VARIABLE ARRAY CLASSES The va_pbuf method will return a pointer to the actual data buffer (ie the Rc_vaxvar buf field). The va_copy method takes a buffer address (to take the copied record) rather than the address of an Rc_vaxvarR struct. When a record is inserted, a cell is allocated for the variable length record data and an index record is inserted - the capacity of the index may need to be increased. When a record is deleted, the record data cell is freed and the corresponding index record is deleted. Deletions do not automatically reduce the record capacity of the index but the index capacity may be reduced manually using va_compress or va_capacity. There is no means of controlling the capacity of the record data storage. On a 16-bit address machine, the overhead per record is 4 bytes for the Rc_vaxvar record and at least 2 bytes for the allocated cell. However, any zero length records do not have an associated record value cell and, in this case, the corresponding but (address) field of the rc_vaxvar struct is guaranteed to be nuLL. The vaxvar class is suitable for short to medium length arrays or for large arrays which have a known maximum capacity (in terms of the number of records required). It is not suitable for arrays which can dynamically grow to a large number of records because the index is in a single cell. The vaxvars class (which has a segmented index) should be used when there is a possibility of growth to a large number of records. Compared to vastr, vaxvar has the following advantages: ® vaxvar can store arbitrary record data, which may include zero bytes e random access to vaxvar records is efficient @ vaxvar records may be exchanged efficiently since only the corresponding index items are exchanged - the sort method is thus very efficient e the data for each record is stored in a separately allocated cell, so heap fragmentation is less likely to be a problem Compared to vastr, vaxvar has the following disadvantages: e the record overhead in vaxvar is at least six bytes compared to one byte in vasTR e the Rc_vaxvar descriptor which is used to describe a record is often less convenient than the address of a zero terminated string, but the va_pbuf method overcomes this quite well e —vaxvar records do not automatically provide a zero terminator Class definition Defined in sub-category file varray.cl (generated header file varray.g). CLASS vaxvar vaflat Indexed variable length record variable arrays - one record per heap cell { REPLACE va_test REPLACE va_copy REPLACE va_reclen REPLACE va_replace REPLACE va_init REPLACE va_deletem REPLACE va_insertm REPLACE va_pbuf TYPES { typedef struct { UWORD len; record length UBYTE *buf; record data } RC_VAXVAR; } Property None. OLIB REFERENCE VAXVAR methods & VA_INIT Initialise VOID va_init (UINT gran) ; Initialise the array by supersending the va_1n1T message to the varLat object, passing the size of an RC_VAxvaR Structure as the record size and gran as the granularity. The subclassed variat array provides the index used by the vaxvar object. No capacity is actually allocated until the first insertion, hence no out of memory errors can occur. The granularity is significant when an insertion requires an increase in capacity; the increase is such that the capacity, in records, is made an exact multiple of the granularity. Making this value larger means that the index array cell needs to be reallocated less frequently (hence less heap fragmentation) as a result of insertions, but more memory may be wasted in unused capacity. & VA_TEST Compare two records by pointer INT va_test (VOID *precl, VOID *prec2); Compare the record pointed to by prec2->buf with the record at prec1->buf, on the assumption that they contain text, exactly as for the vaRooT va_test method. The basis for the comparison is subject to the contents of varoot .key, set by va_key, as follows: if varoot .key.1len==0 it uses p_scmp df varoot.key.fold is FALSE), or p_scmpi af varoot.key.fold is TRUE). if varoot .key.len>0 it uses p_bemp (if varoot.key.fold iS FALSE), OF p_bcempi af varoot.key.fold is TRUE). The default is to use p_scmp. Returns the logical equivalent of (*preci1->buf-*prec2->buf) - that is, zero if the two records are equal, negative if *preci->buf is before (less than) *prec2->buf, positive if after. If key. desc iS TRUE this result is reversed. & VA_DELETEM Delete a sequence of records VOID va_deletem(UINT recno, UINT nrecs); Delete the sequence of nrecs records starting at record recno. The process of deletion is two fold: first, the records defined by the rc_vaxvar elements are found and any allocated cell (buffer data) is freed; Second, the index elements are deleted by supersending a va_DELETEM message. VA_INSERTM Insert a sequence of records VOID va_insertm(UINT recno, RC_VAXVAR *prec, UINT nrecs); Insert the sequence of nrecs records pointed to by prec before record recno, where recno is between zero and the number of records inclusive. The record sequence pointed to by prec must be a contiguous array of nrecs RC_VAXVAR Structs. The insertion of records is a two stage process. Firstly the index entries are inserted by supersending a VA_INSERTM message to the varLat superclass. If this is successful, a heap cell is allocated for each record to be inserted, the data is copied into each cell and the cell handle written to the rc_vaxvar buf field. If an allocation fails, all entries made so far are removed, their allocated cells being freed. Inserts no records and calls p_leave (E_GEN_NOMEMoRY) if there is not enough memory available to hold all the additional records. 5 VARIABLE ARRAY CLASSES VA_REPLACE VOID va_replace(UINT recno, RC_VAXVAR *prec); Replace record Replace record recno with the record described by the rc_vaxvar struct at prec, by freeing the original record cell, allocating a new cell of the appropriate size and replacing the index record with the new descriptor. Calls p_leave (E_GEN_NOMEMoRY) , Without modifying the original record, if there is not enough memory available to hold the replacement record. & VA_COPY Copy a record UINT va_copy(UINT recno, VOID *pbuf); Copy record number recno by supersending itself a va_copy message to get the record descriptor and then using p_bcpy to copy the record data to pbuf. Returns the length of the data copied to pbut. & VA_RECLEN UINT va_reclen(UINT recno); Get record length Returns the record length of record recno. & VA_PBUF Point to record data VOID *va_pbuf (UINT recno); Returns a pointer to the record data, read from the bur field of the corresponding Rc_vaxvar struct. It should be noted that the va_prec method (provided by the variart superclass) returns a pointer to the RC_VAXVAR Struct. VAXVARS VAROOT VAFIX VASEG VAXVARS nrec ke destroy ad va_test va_count va_delete va_sort va_key va_findisgq va_insertisq va_append va_insert va_search va_compare va_reset va_test va_compress a ee Farinsertm va_capacity va_prec wa pbut va_copy va_reclen va_replace va_init va_deletem va_insertm va_pbuf The vaxvars class is identical to the vaxvar class except that it subclasses the vaszc class (a segmented array) for its index rather than var.at (a flat array). In other words, the index records are placed in a segmented buffer. The vaxvars class should be used in preference to vaxvar when there is the possibility of a large number of records. OLIB REFERENCE Class definition Defined in sub-category file varray.cl (generated header file varray.g). CLASS vaxvars vaseg Indexed variable length record variable arrays - one record per heap cell Uses a segmented array for an index REPLACE va_test=vaxvar_va_test REPLACE va_copy=vaxvar_va_copy REPLACE va_reclen=vaxvar_va_reclen REPLACE va_replace=vaxvar_va_replace REPLACE va_init=vaxvar_va_init REPLACE va_deletem=vaxvar_va_deletem REPLACE va_insertm=vaxvar_va_insertm REPLACE va_pbuf=vaxvar_va_pbuf Property None. VAXVARS methods See VAXVAR methods. CHAPTER 6 EDITABLE DOCUMENTS The EPROOT, EPFLAT and EpsEc classes provide the means of storing, reading and editing, in memory, the text of a document. The text must always have a terminating zero but may otherwise be of any length up to a maximum of 65535 characters, subject to memory constraints. The methods support the use of an instance of (a subclass of) EPFLAT or EPSEG as a clipboard. Text may be cut or copied into such a clipboard from a document or pasted from a clipboard into a document. Document content The concepts of words, paragraphs and blocks are built into the document content model, where: e® a paragraph is any sequence of characters delimited by a paragraph delimiter. A paragraph delimiter is an ASCII 0, 1, 2 or 3, a value of 0 being by far the most commonly used. The final paragraph delimiter in the document is always an ASCII zero. e aword is any sequence of characters delimited by one or more word delimiter characters. A word delimiter is either a paragraph delimiter, or a whitespace character (for which p_isspace returns TRUE). e a block is a sequence of paragraphs delimited by paragraphs that are either empty or, if not empty, contain only whitespace characters. The terminating ASCII zero is generally not regarded as part of the editable content. It is not, for example, included in the document length as returned by an ep_sense_len method. Its presence is fundamental to the operation of all classes that subclass EPRoot and it should never be deleted. Addressable character positions A position in an EPFLAT or EPSEG document is considered, in general, to mark the point between two adjacent characters. Thus, character position 3 is interpreted as being between the third and fourth characters. Character position zero is before the first character in the document. Inserting at position 9, say, will insert text after the ninth character: deleting the characters between positions 3 and 5 will delete the fourth and fifth characters. The last addressable character position is immediately before the final paragraph delimiter (always an ASCII zero). Usage Instances of (subclasses of) EPFLAT and EPsEc are widely used in all SIBO machines to contain editable text. Examples range from the text in a dialog edit box, to the text of a word processor document. In general, The epriat class should be used for small amounts of text, or for documents with a limited variability of content, whereas the EpsEc class should be used for larger documents, or for those with a wide dynamic content range. (Compare this with the recommended use of the varLat and vaszc variable array classes.) OLIB REFERENCE Precursors An understanding of the following topics would prove helpful: e =the p_enter and p_leave error handling services e for EPsEG, the scpur segmented buffer class Class diagram fees ¢ eproot / ~ S22) ~ COS. pee fB ¢ epflat / ~ epseg y y sgbuf / > js 4.43 es ) Na ae Los Sees — EPROOT ep_set_text ep_sense_text ep_scan_word ep_capacity ep_word_count ep_scan_para ep_compress ep_para_count ep_init ep_scan_block ep_sense_len ep_add_para ep_sense_chars ep_copy_indent ep_back_chars ep_copy_to_front ep_insert ep_copy_to_back ep_delete ep_paste ep_extract ep_mod_chars ep_clear The Eproot abstract class provides the basic methods for manipulating the text of a document. 6 EDITABLE DOCUMENTS Class definition Defined in sub-category file edit.cl (generated header file edit. g). CLASS eproot root Editable paragraphs (abstract class) { ADD ep_set_text Set the contents, replacing any previous contents ADD ep_scan_word Scan by words ADD ep_word_count Return word count ADD ep_scan_para Scan by paragraphs ADD ep_para_count Return paragraph count ADD ep_scan_block Scan by blocks ADD ep_add_para Append a paragraph given its content ADD ep_copy_indent Copy indent from previous paragraph ADD ep_copy_to_front Copy range to front of provided clipboard ADD ep_copy_to_back Copy range to back of provided clipboard ADD ep_paste Paste from provided clipboard, update position ADD ep_mod_chars Various mods to a range of characters ADD ep_sense_text Copy out all the data and return its length ADD ep_capacity=p_dummy Adjust storage to stated capacity DEFER ep_compress Compress storage to minimum possible DEFER ep_init Prepare an empty editable object DEFER ep_sense_len Return length of data DEFER ep_sense_chars Provide pointer to following contiguous data DEFER ep_back_chars Provide pointer to previous contiguous data DEFER ep_insert Insert block of characters DEFER ep_delete Delete range of text DEFER ep_extract Extract data into buffer DEFER ep_clear Empty the object of all contents CONSTANTS { EP_SCAN_BACKWARDS Ox01 Scan backwards if set EP_SCAN_STAY 0x02 Will stay put if already at a boundary EP_SCAN_TO_BEGIN 0x04 Stops at beginning of unit EP_SCAN_TO_END 0x08 Stops at end of unit EP_SCAN_JOIN_DELIM 0x10 Sequence of delimiters count as one EP_SCAN_NOT_TO_END 0x20 Do not scan past the final terminator EP_RETURN_LENGTH -1 special meaning for ep_sense_chars EP_MOD_TOLOWER 0 fold to lower case EP_MOD_TOUPPER 1 fold to upper case EP_MOD_WRAP 2 join paragraphs by converting zeros to spaces } PROPERTY { UWORD maxlen; Maximum permissible length of text } } Property eproot.maxlen the maximum permissible length of the text. All subclasses must set this field, normally in an ep_init method EPROOT methods EP_SET TEXT Set content VOID ep_set_text (TEXT *buf, UINT len); Replace any existing text with a single paragraph containing 1en characters copied from *buf. The value of 1en should exclude any trailing paragraph terminator. Calls p_1eave for out of memory errors. In this event, none of the existing text will have been replaced. OLIB REFERENCE & EP_SCAN_ WORD Scan by word UINT ep_scan_word(UWORD *ppos, UINT flags); Scan, from character position *ppos, by one word, updating *ppos to the new character position and returning the number of characters skipped by the scan. There is no need for the initial position to be at a word boundary. The value of f1ags may be any combination of the following: EP_SCAN_BACKWARDS EP_SCAN_STAY EP_SCAN_TO_BEGIN EP_SCAN_TO_END EP_SCAN_JOIN_DELIM EP_SCAN_NOT_TO_END Scan backwards if set, otherwise scan forwards Do not scan if already at a word boundary Scan to the beginning of a word Scan to the end of a word Treat a sequence of word delimiters as a single delimiter Do not scan past the final terminator & EP_WORD COUNT UINT ep_word_count (VOID) ; Count words Return a count of the total number of words in the document. & EP_SCAN_PARA UINT ep_scan_para(UWORD *ppos, UINT flags); Scan by paragraph Scan, from character position *ppos, by one paragraph, updating *ppos to the new character position and returning the number of characters skipped by the scan. There is no need for the initial position to be at a paragraph boundary. The value of f1ags may be any combination of the following: EP_SCAN_BACKWARDS Scan backwards if set, otherwise scan forwards EP_SCAN_STAY Do not scan if already at a paragraph boundary EP_SCAN_TO_BEGIN Scan to the beginning of a paragraph EP_SCAN_TO_END Scan to the end of a paragraph EP_SCAN_JOIN_DELIM Treat a sequence of paragraph delimiters as a single delimiter EP_SCAN_NOT_TO_END Do not scan past the final terminator & EP_PARA_COUNT UINT ep_para_count (VOID) ; Count paragraphs Return a count of the total number of paragraphs in the document. © EP_SCAN BLOCK UINT ep_scan_block(UWORD *ppos, UINT flags); Scan by block Scan, from character position *ppos, by one block, updating *ppos to the new character position and returning the number of characters skipped by the scan. The initial position is assumed to be at a paragraph boundary. 6 EDITABLE DOCUMENTS The value of f1ags may be any combination of the following: EP_SCAN_BACKWARDS Scan backwards if set, otherwise scan forwards EP_SCAN_STAY Do not scan if already at a block boundary EP_SCAN_TO_BEGIN Scan to the beginning of a block EP_SCAN_TO_END Scan to the end of a block EP_SCAN_JOIN_DELIM Treat a sequence of block delimiters as a single delimiter EP_SCAN_NOT_TO_END Do not scan past the final terminator EP_ADD_PARA Append a paragraph VOID ep_add_para(TEXT *buf, UINT len); Append a paragraph containing 1en bytes of text copied from *buf. The text in buf is appended more efficiently if it is supplied as a zero terminated string - when len should be equal to p_sien (buf) - but the terminating zero is not mandatory. Calls p_leave, without inserting anything, if there is insufficient memory available to append the paragraph. EP_COPY_INDENT Copy whitespace indentation UINT ep_copy_indent (UWORD *ppos) ; Copy any leading whitespace from the previous paragraph (which is assumed to exist) to the character position indicated by *ppos. This position will normally be the start of a paragraph. The value of *ppos is incremented by the number of characters inserted. Returns the number of characters inserted. Inserts nothing and calls p_1eave if there is insufficient memory to perform the insertion. Returns zero if the insertion is successful. The method is suitable for being called under the protection of p_enter. EP_COPY_TO_FRONT Copy range to front of clipboard INT ep_copy_to_front (UINT posl, UINT pos2, PR_ROOT *clip); Copy the range of characters between positions posi and posz2 to the front (position zero) of the clipboard clip, where clip is assumed to be the handle of an instance of (a subclass of) EPROOT Or EPSEG. Inserts nothing in the clipboard and calls p_ieave if there is insufficient memory to perform the insertion. Returns zero if the insertion is successful. The method is suitable for being called under the protection of p_enter. EP_COPY_TO BACK Copy range to back of clipboard INT ep_copy_to_back(UINT posl, UINT pos2, PR_ROOT *clip); Copy the range of characters between positions posi and pos2 to the back (before the terminating zero) of the clipboard clip, where clip is assumed to be the handle of an instance of (a subclass of) EPRooT or EPSEG. Inserts nothing in the clipboard and calls p_ieave if there is insufficient memory to perform the insertion. Returns zero if the insertion is successful. The method is suitable for being called under the protection of p_enter. OLIB REFERENCE EP_PASTE Insert from clipboard INT ep_paste(UWORD *ppos, PR_EPROOT *clip); Insert the contents of the clipboard clip, assumed to be the handle of an instance of (a subclass of) EPROOT Of EPSEG, at position *ppos. The value of *ppos is updated to the end of the inserted text. Inserts nothing and calls p_ieave if there is insufficient memory to perform the insertion. Returns zero if the insertion is successful. The method is suitable for being called under the protection of p_enter. & EP_MOD_CHARS Modify characters in a range VOID ep_mod_chars(UINT posl, UINT pos2, UINT mod); Modify the characters between positions pos1 and pos2. The modification depends on the value of moa, which may be one of: EP_MOD_TOUPPER convert characters to upper case, with p_toupper EP_MOD_TOLOWER convert characters to lower case, with p_tolower EP_MOD_WRAP convert each paragraph delimiter character to a space (ASCII 32) character & EP_SENSE TEXT Copy text to buffer UINT ep_sense_text (TEXT *buf); Copy the entire text of the document, including the terminating zero, to *buf. It is the user's responsibility to ensure that the buffer is of sufficient length. Returns the number of characters copied, excluding the terminating zero. & EP_CAPACITY Set document capacity VOID ep_capacity(UINT len); This method does nothing, which is the appropriate action for subclasses using segmented storage (that is, EPSEG or a subclass of EPSEG). Subclasses using storage in a single allocated segment should subclass this method (see, for example, EPFLAT'S ep_capacity method). Deferred EPROOT methods The deferred methods, listed below, are all fully described in the following documentation of the EPFLAT and Epssc Classes. EP_INIT Initialise EP_SENSE_LEN Sense document length EP_SENSE_CHARS Sense characters forwards EP_BACK_CHARS Sense characters backwards EP_INSERT Insert characters EP_EXTRACT Copy out characters EP_DELETE Delete characters EP_CLEAR Clear the document EP_COMPRESS Compress allocated storage EPFLAT maxlen ep_set_text ep_sense_text ep_scan_word ep_word_count ep_scan_para ep_para_count ep_scan_block ep_add_para ep_copy_indent ep_copy_to_front ep_copy_to_back ep_paste ep_mod_chars EPFLAT stores the document text contiguously in a single allocated cell. EPFLAT destroy ef_granularity ef_sense_buf ep_init ep_sense_len ep_sense_chars ep_back_chars ep_insert ep_extract ep_delete ep_clear ep_compress ep_capacity 6 EDITABLE DOCUMENTS This class is intended for use either to store relatively small amounts of text, or where the dynamic range of the document size is small. A typical example is to store the text of a dialog edit box. Class definition Defined in sub-category file edit.cl (generated header file edit. g). CLASS epflat eproot Editable paragraphs - stored in a single allocated cell REPLACE REPLACE REPLACE REPLACE REPLACE REPLACE REPLACE REPLACE REPLACE REPLACE REPLACE ADD ef_g ADD ef_s PROPERTY { UWOR UWOR UWOR TEXT } } Property epflat.alen epflat.gran epflat.len epflat.buf destroy ep_init ep_sense_len ep_sense_chars ep_back_chars ep_insert ep_extract ep_delete ep_clear ep_compress ep_capacity ranularity ense_buf Overwrite the default granularity More convenient than ep_sense_chars D alen; D gran; D len; *buf; length of allocated cell granularity to grow by length of text stored address of text buffer the size, in bytes, of the allocated cell. This should not be accessed by a subclass. the granularity, in bytes. The allocated cell is grown, when necessary, in multiples of this value. This should not be accessed by a subclass. the number of bytes of stored text. This should be treated as a read-only field by a subclass. a pointer to the start of the buffer containing the text. This should be treated as a read-only field by a subclass. OLIB REFERENCE EPFLAT methods & DESTROY Destroy VOID destroy (VOID) ; Free the allocated buffer and supersend the pestroy message. EP_INIT Initialise VOID ep_init (UINT maxlen) ; Initialise the instance of EPpFLat to be suitable to contain up to maxlen bytes of text (typically the text will not exceed that which can be displayed on a single line). Sets eproot .maxlen tO maxlen and epflat.gran to a default value of eight bytes. Allocates a minimum size buffer and inserts a single nun character. Calls p_leave if there is insufficient memory to allocate the buffer. & EP_SENSE LEN Sense document length INT ep_sense_len (VOID) ; Returns the number of characters in the document, excluding the terminating zero. & EP_ SENSE CHARS Sense characters forwards UINT ep_sense_chars (TEXT **pbuf, UINT pos, UINT n); Write, to *pbuf, the address of the character at position pos. Returns the smaller of n and the number of contiguously stored characters at *pbuf, including the zero that terminates the document. Note that for compatibility with the zpszc class, the interface to this method does not assume that the entire text of the document is stored contiguously. & EP_BACK_CHARS Sense characters backwards UINT ep_back_chars (TEXT **pbuf, UINT pos, UINT n); Write, to *pbuf, the address of the character whose position is n characters in front of position pos; if n is greater than the number of characters in front of position pos, the address of the first character is taken. Returns the number of contiguous characters at *pbuf up to, but not including, the character at position pos. This value will not exceed n. Note that, for compatibility with the Epszc class, the interface to this method does not assume that the entire text of the document is stored contiguously. EP_INSERT Insert characters INT ep_insert (UINT pos, TEXT *buf, UINT len); Insert 1en characters from *buf, at character position pos. If pos is -1, the characters are inserted at the end of the document (before the terminating zero). This feature is not replicated in the ep_insert method of the epsse class. Inserts nothing and calls p_ieave if there is insufficient memory to perform the insertion (with an E_GEN_NOMEMoRY error) or if the resulting document length were to exceed eproot .maxlen (with an E_GEN_OVER error). Returns zero if the insertion is successful. The method is suitable for being called under the protection of p_enter. 6 EDITABLE DOCUMENTS & EP_EXTRACT Copy out characters VOID ep_extract (UINT pos, TEXT *buf, UINT len); Copy len characters to *buf, starting with the character at position pos. The user is responsible for ensuring that the buffer is of sufficient length to contain the text. Note that no check is made to ensure that 1en characters are available at position pos. & EP_DELETE Delete characters VOID ep_delete(UINT posl, UINT pos2); Delete the characters between positions pos1 and pos2, without making any attempt to reduce the size of the allocated buffer. If pos2 is -1 then all characters between position pos1 and the end of the document are cleared. If pos2 is less than or equal to pos1 then the method does nothing. & EP_CLEAR Clear the document VOID ep_clear (VOID) ; Remove any text. This leaves a document containing a single terminating zero. No attempt is made to reduce the size of the allocated buffer. EP_COMPRESS Compress allocated storage VOID ep_compress (VOID) ; Reduce the size of the allocated cell to the minimum required to contain the document, subject to the restraint of the current value of epflat.gran. The cell will never be smaller than epfiat.gran bytes in length. This method can only fail (by calling p_1eave) if it follows an ErF_GRANULARITY message that alters the granularity such that the ep_compress method causes the allocated cell to increase in size. In general (and certainly if the ef_granularity method is never called) it is safe to assume that this method will never fail. EP_CAPACITY Set document capacity VOID ep_capacity(UINT len); Set the document capacity (using £_realloc) to 1en bytes, rounded up to be a multiple of epflat.gran. Calls p_leave if there is insufficient memory to reallocate the cell. & EF_GRANULARITY Set buffer granularity VOID ef_granularity(UINT gran); Set epflat.gran to gran or, if gran 1s zero, set epflat.gran to l. No attempt is made to alter the current size of the allocated buffer which may, therefore, be incompatible with the new granularity. & EF_SENSE BUF Sense start of data UINT ef_sense_buf (TEXT **pbuf) ; Write, to *pbuf, the address of the first byte of the document text, returning the length of the text, excluding the terminating zero. This method, unlike ep_sense_chars, takes advantage of the fact that the whole of the text is stored contiguously. OLIB REFERENCE EPSEG EPROOT Ge ep_set_text ep_scan_word ep_word_count ep_scan_para ep_para_count ep_scan_block ep_sense_text ep_capacity ep_init ep_sense_len ep_sense_chars ep_back_chars ep_insert ep_extract ep_add_para ep_delete ep_clear ep_copy_indent ep_copy_to_front epiinsert ep_copy_to_back ep_compress ep_paste ep_mod_chars EPSEG Stores the document text in a segmented buffer using an instance of scBuF. This class is intended for use either to store large amounts of text, or where the dynamic range of the document size is potentially large. A typical example is to store the text of a text processor document. Class definition Defined in sub-category file edit.cl (generated header file edit. g). CLASS epseg eproot Editable paragraphs - segmented storage REPLACE ep_init REPLACE ep_sense_len REPLACE ep_sense_chars REPLACE ep_back_chars REPLACE ep_insert REPLACE ep_extract REPLACE ep_delete REPLACE ep_clear REPLACE ep_compress PROPERTY 1 { PR_SGBUF *b; } handle of buffers data } Property the handle of an instance of the scBur segmented buffer class in which the text is stored. It should be regarded as read only. epseg.b EPSEG methods EP_INIT VOID ep_init (UINT maxlen) ; Initialise Initialise, by setting eproot .maxlen tO maxlen, creating an instance of scBur and initialising it with a fixed granularity of 64 bytes and then inserting a single nu. character. Calls p_leave if there is insufficient memory. 6-10 6 EDITABLE DOCUMENTS & EP_SENSE LEN Sense document length UINT ep_sense_len (VOID) ; Returns the number of characters in the document, excluding the terminating zero. & EP_SENSE CHARS Sense characters forwards UINT ep_sense_chars (TEXT **pbuf, UINT pos, UINT n); Write, to *pbuf, the address of the character at position pos. Returns the smaller of n and the number of contiguously stored characters at *pbuf, including the zero that terminates the document. & EP_BACK_CHARS Sense characters backwards UINT ep_back_chars (TEXT **pbuf, UINT pos, UINT n); Write, to *pbuf, the address of the character whose position is n characters in front of position pos; if n is P P greater than the number of characters in front of position pos, the address of the first character is taken. Returns the number of contiguous characters at *pbuf up to, but not including, the character at position pos. This value will not exceed n. EP_INSERT Insert characters INT ep_insert (UINT pos, TEXT *buf, UINT len); Insert 1en characters from *buf, at character position pos. Inserts nothing and calls p_ieave if there is insufficient memory to perform the insertion (with an E_GEN_NOMEMoRY error) or if the resulting document length were to exceed eproot .maxlen (with an E_GEN_OVER error). Returns zero if the insertion is successful. The method is suitable for being called under the protection of p_enter. & EP_EXTRACT Copy out characters VOID ep_extract (UINT pos, TEXT *buf, UINT len); Copy len characters to *buf, starting with the character at position pos. The position pos must lie within the range of the segmented buffer or else p_panic(P_PANIC_sGBUF_1) Will be called. The user is responsible for supplying a buffer of sufficient length to contain the text. & EP_DELETE Delete characters VOID ep_delete(UINT posl1, UINT pos2); Delete the characters between positions pos1 and pos2, by sending the owned instance of scpur an SB_DELETE message. If the position pos1 lies outside the range of the segmented buffer, p_panic (P_PANIC_SGBUF_1) Will be called. Similarly if the position pos2 lies outside the range of the segmented buffer, p_panic (P_PANIC_SGBUF_2) will be called. & EP_CLEAR Clear the document VOID ep_clear (VOID) ; Remove any text (by sending the owned instance of scBuF an SB_DELETE message) leaving a document containing a single terminating zero. & EP_COMPRESS Compress allocated storage VOID ep_compress (VOID) ; Reduce the size of the owned instance of scpur to the minimum required to contain the document, consistent with its granularity of 64 bytes, by sending it an sB_compREss message. CHAPTER 7 RESOURCE FILES RSCFILE pcb ix offset hftree destroy rs_init rs_read rs_read_buf The RScFILE class provides a set of services to access the contents of a resource file. Resource files are described in the Resource Files chapter of the Additional System Information manual. As mentioned there, a resource file may be embedded in an image file and may optionally be Huffman code compressed. An application that needs a resource file and also uses an application manager (appman) will generally pass the FLG_APPMAN_RSCFILE flag to the APPMAN am_init method. This causes an instance of the RSCFILE class to be created automatically. The application can then read resources by means of the application manager's am_load_resource and am_load_res_buf methods which offer greater functionality than the RSCFILE rs_read and rs_read_buf methods. Such users will not require detailed knowledge of the RSCFILE Class. Precursors The reader is assumed to understand: e =the p_enter and p_leave error handling services Class definition The RSCFILE class subclasses Root and is defined in the sub-category file appman.cl (with generated header file appman.g). CLASS rscfile root Basic access to a resource file which may be embedded in an image file { REPLACE destroy Close channel and destroy ADD rs_init Open a resource file ADD rs_read Read a record into an allocated cell ADD rs_read_buf Read a record into a buffer TYPES { typedef struct { UWORD pos; Index file position UWORD len; Index length } PR_RSCFILE_HEAD; } PROPERTY { UBYTE *pcb; resource file channel PR_RSCFILE_HEAD ix; header containing index position and length UWORD offset; file offset of start of resource data ULONG hftree; Huffman tree data } OLIB REFERENCE Property rscfile.pcb the currently opened resource file channel handle. It should not be accessed by a subclass. rscfile.ix the resource file header, containing the index position and length. It should not be accessed by a subclass. rscfile.offset the offset from the start of the file to the start of the resource data (zero unless the file is embedded in an image file). It should not be accessed by a subclass. rscfile.hftree the Huffman tree data. It should not be accessed by a subclass. RSCFILE methods J DESTROY Destroy VOID destroy (VOID) ; Close the currently opened resource file and supersend the pEstRoy message. RS_INIT Initialise INT rs_init (UBYTE *pname) ; Open a channel to a resource file. The string pointed to by *pname should be the name of either the resource file itself, or of an image file containing, in its second add-file slot, an embedded resource file. (See the Resource Files chapter of the Additional System Information manual.) Writes the appropriate values to rscfile.offset, rscfile.ix and, if the resources are Huffman encoded, rscfile.hftree. Calls p_leave on error, otherwise returns zero. The method is suitable for being called under the protection of p_enter. RS READ Allocate buffer and read resource INT rs_read(INT rid, UBYTE **ppdata) ; Allocate a buffer and read into it the resource with resource id rid. The length of the resource is read from the file and a buffer of this length is allocated. The resource is then read into this buffer and the address of the buffer is written to *ppdata. If the resource file record is Huffman encoded then it is decoded before being written to the buffer. The method calls p_leave on any error (which will be either a memory allocation error or an error while attempting to read the file). It is guaranteed that, on error, no memory will have been allocated and nothing will have been written to *ppdata. Returns the length of the resource, including the terminating zero if the resource is a string. RS READ BUF Read resource INT rs_read_buf (INT rid, UBYTE *buf); Read the resource with resource id ria into the buffer pointed to by but. If the resource file record is Huffman encoded then it is decoded before being written to the buffer. It is the user's responsibility to ensure that the buffer is of sufficient length to contain the resource. The method calls p_leave on error (which will be an error while attempting to read the file). Returns the length of the resource, including the terminating zero if the resource is a string. CHAPTER 8 BINARY FILE MANAGEMENT The classes described in this chapter provide methods for reading and writing signatured binary files. Such files contain a 22 byte standard header consisting of: e a 16 byte file signature (all 16 bytes are significant) e a2 byte file version number e a2 byte offset from the start of the file to the end of the header (to allow for future expansion of the header) e a2 byte runtime version number Each version number is a hexadecimal number in the form xyyvr, where: x is the major version number (4 bits) YY is the minor version number (8 bits) F is the release type, A (alpha) B (beta) or F (final) For example, 0x123A is an alpha release of version 1.23. The runtime version number is intended to specify the minimum version of runtime software (for example, OPL) that is required to process the file. If this field is not used it should be set to zero. Database files are a particular type of signatured binary file. Further information relating to this type of file may be found in the Database Files chapter of the PLIB Reference manual and in the ISAM Reference manual. Precursors An understanding of the following topics would prove helpful: e the PLIB binary file services, as described in the Files chapter of the PLIB Reference manual. e =the p_enter and p_leave error handling services Class diagram om “~~ — — bfile : Co iden : TAN. {tIvfile ) serfila. / ~ ) ee ce OLIB REFERENCE BFILE pcb rbuf rlen offset destroy fi_close fi_read fl_set_buf_len fl_sense_data fi_open fl_rewind The methods of the pr1z class provide the basic means of creating, opening, validating and reading signatured binary files with arbitrary content. BFILE must be subclassed to provide additional methods if it is necessary to write to the file. Class definition Defined in sub-category file tlvfile.cl (generated header file tlvfile.g). CLASS bfile root { REPLACE destroy Close file then destroy ADD fi_close Free any buffers ADD fi_read Read from file into internal buffer ADD fl_set_buf_len Ensure internal buffer is at least len bytes ADD fl_sense_data Get length and address of data ADD fi_open Open binary file and check signature ADD fl_rewind Reposition to first byte after header CONSTANTS { OP_BFILE_ID_SIZE 16 size of text ID } TYPES { typedef struct { TEXT fid[OP_BFILE_ID_SIZE]; plain text application ID UWORD vers; file version number UWORD offset; abs. file offset to end of header UWORD rtvers; minimum runtime version } } PROPERTY { UBYTE UBYTE UWORD UWORD } } Property bfile.pcb bfile.rbuf bfile.rlen bfile.offset OP_BFILE_FSIG; *pcb; File channel *rbuf; Allocated record buffer rlen; Length of data in read buffer offset; the handle of a currently open file, or nun. It should not be accessed by any subclass. a pointer to an allocated buffer into which file data is read. the number of bytes of valid data in the allocated buffer. the byte offset from the start of the current file to the first byte after the file signature. 8 BINARY FILE MANAGEMENT BFILE methods & DESTROY Destroy VOID destroy (VOID); Send an FI_cLosE message and then supersend the DEsTRoy message. Fl_OPEN Open file INT fi_open(TEXT *pname, UINT mode, OP_BFILE_SIG *psig) ; Open the binary file whose full file specification 1s pointed to by pname. The value of mode may be any combination of the PLIB file mode flags appropriate for a binary file (it must include p_rsTREAM or, more rarely, P_FSTREAM_TEXT). If a new file is being opened (mode contains either P_FCREATE Or P_FREPLACE) the file signature at «psig is written to the newly opened file. Otherwise, the file signature is read from the file and compared with the file signature at *psig. If the file signature read from the file is the wrong length, or if the two 16-byte file IDs do not match exactly (all sixteen bytes are compared) p_leave (E_FILE_INVALID) is called. The validation of the remaining signature fields will vary with the application and is left to the caller. To facilitate this validation, the signature read from the file is written to *psig. Once the file has been successfully opened, the file offset to the first byte following the file header (psig->offset) is written to bfile.offset. Returns zero if successful, or a negative error number for any error other than the E_FILE_INVALID error described above. & FI_CLOSE Close file VOID fi_close (VOID) ; Close any open file, freeing any allocated buffer. It is safe to send an FI_CLOSE message even if there is no open file. Fl_READ Read from file INT fi_read(UINT len); Read len bytes from the current position in the currently open file into the allocated buffer. Sends an FL_SET_BUF_LEN message to ensure that the buffer has room for at least 1en bytes before reading the data. If this fails, p_1eave (E_GEN_NOMEMORY) is called. If the read is successful, sets bfile.rlen to contain the number of bytes read into the buffer. Returns the number of bytes read, or a negative error. FL_SET BUF_LEN Set buffer size VOID fl_set_buf_len(UINT len); Reallocate, if necessary, the allocated buffer pointed to by bfile.rbuf to ensure that it has room for at least 1en bytes. This method will never reduce the size of the buffer. Calls p_leave if there is insufficient memory to reallocate the buffer. OLIB REFERENCE & FL_SENSE DATA Sense record data UINT fl_sense_data(UBYTE **pbuf) ; Write to *pbuf the address of the allocated buffer and return the length of the data last read into it by the fi_read method. The return value will be zero and the value written to *pbuf will be nut if there is no currently open file, or if no FI_READ message has been received. FL_REWIND Reposition to start VOID fl_rewind (VOID) ; Set the current file position to the first byte after the file header. TLVFILE TLVFILE rbuf rlen offset destroy fi_open fi_close l1_rewind fi_read l_write_rec fl_set_buf_len fl_sense_data _delrec _—count _read_by_type _set_rec 1 sense_rec FoFH FH FH FH Fh EF SF l_replace The TLvr1.e class provides support for signatured binary files that contain type-length-value (TLV) records. Following the standard header, the file is considered to be made up of records, each of which has a 2 byte header specifying the record type and length. The most significant nibble of the word contains the record type, in the range 0-15. The remaining three nibbles contain the record length and is, therefore, restricted to a maximum of 4K bytes. Records of type 0 are considered to be deleted records. Records of type 15 (OxOf) are reserved to represent non-valid records and are treated as though they are deleted records. For efficient record access, the record number of the next record to be read and the current file position are stored in property. TLV files are designed to be Flash-friendly, i.e. they may be stored and manipulated efficiently in Flash SSDs (or any other EPROM medium). A TLV file stored on such a medium may be modified by appending, deleting or replacing records without having to make a new copy of the entire file. Although TLV files may contain in excess of 4,000,000,000 records, TLVF ILE is ideally suited to manipulating files which contain a relatively small number of records. If the file contains a large number of records, operations which involve non-sequential access may take an extended time to return. Database files are a form of TLV file with a particular file signature header and specific record content. Alternative means of manipulating such files are described in the Database Files chapter of the PLIB Reference manual and also in the ISAM Reference manual. Class definition 8 BINARY FILE MANAGEMENT Defined in sub-category file tlvfile.cl (generated header file tivfile. g). CLASS tlvfile bfile { REPLACE fi_open REPLACE fl_rewind ADD ADD ADD ADD ADD ADD ADD CONS TYPE PROP } Property fl_write_rec Open file Reposition to first record, reset property Write a TLV record fl_delrec Delete a record f1l_count Get record count fl_read_by_type Read record of specified type fl_set_rec Set the current record number fl_sense_rec fl_replace Sense the current record number Replace a record TANTS TLV_TYPE_UNKNOWN 0x10 TLV_TYPE_INVALID Ox0f TLV_TYPE_DELETED 0x00 TLV_TYPE_NORMAL 0x01 TLV_TYPE_FIELDS 0x02 TLV_TYPE_SHIFT 12 TLV_TYPE_MASK Oxf000 } Ss typedef struct { OP_BFILE_FSIG fsig; Binary file signature UWORD types; Valid types } OP_TLVFILE; typedef struct { UBYTE UINT len; INT type; } OP_TLV_REC; ERTY { UWORD UWORD UWORD ULONG ULONG } tlvfile.typmask tl ae tl ti lvfil lvfil lvfil le. hdlen le.hdt le.pos lvfil ype le.fpos typmask; hdlen; hdtype; Pos; fpos; *Du Es. valid record type mask length of header type from header Next record to be read Current file position, for validation which record types are considered valid. For example, the value 0x32 (bits 1, 4 and 5 set) indicates that records of type 1, 4 and 5 are valid. Records of other types are treated as if they do not exist. the current record length as decoded from the first word of the record. It should not be accessed by a subclass. the current record type as decoded from the first word of the record. It should not be accessed by a subclass. the current record number that corresponds to the file position, tlvfile.fpos. It should not be accessed by a subclass. the current file position. It should not be accessed by a subclass. OLIB REFERENCE TLVFILE methods All methods which read a record assume that the current file position is at the start of a record. Fl_OPEN Open TLV file INT fi_open(TEXT *fspec, UINT mode, OP_TLVFILE *psig): Open the TLV file whose full file specification is pointed to by fspec. The value of mode may be any combination of the PLIB file mode flags appropriate for a binary file (it must include p_rsTREaAM). Opens the file by supersending the r1_oPzn message which, if opening an existing file, validates the 16- byte file signature ID and overwrites psig->fsig (but not psig—>types) with the signature read from the file. Calls p_ieave if the file signature validation fails. Sets tlvfile.typmask to the value of psig->types and sends itself an rL_REWIND message to position to the start of the first record. Returns zero if successful or error values as returned by the Br1Lz superclass. FL_REWIND Reposition to start VOID fl_rewind (VOID) ; Position to the first byte following the file signature. Supersends the rL_REwIND message and then sets tivfile.fpos tO bfile.offset, and tlvfile.pos to zero. Calls p_1eave on error. FL_COUNT Count records VOID fl_count (ULONG *pcount) ; Write to *pcount the number of valid records (those whose types are specified by tivfile.typmask) in the file. Counts the records by scanning the entire file and then sends an rL_REWIND message to reposition to the first record. Calls p_leave on error. FL_WRITE_REC Write a record VOID fl_write_rec(UBYTE *buf, UINT len, UINT type); Append a new record of type type, containing the first 1en bytes of the data pointed to by bue. If any error occurs, the record is either not written or is marked as not being a valid record. All errors result in p_leave being called. FL_SET REC Set current record INT fl_set_rec(UINT lsw, UINT msw); INT fl_set_rec(ULONG recnum) ; (conceptual) Position to, and read into the internal buffer, record number recnum (counting only records of types specified by t1vfile.typmask). The uLONG recnum is actually passed in the message as two UINT parameters, 1sw (least significant word) and msw (most significant word). Returns the positive record type if successful, or =_F1LE_koF if reading past the end of the file. Other file errors result in a call to p_leave. The content of the internal buffer is unpredictable in the event of an error. 8-6 8 BINARY FILE MANAGEMENT & FL_SENSE REC Sense current record number VOID fl_sense_rec(ULONG *prec); Write, to *prec, the record number of the record following the one which has last been read. Writes zero if no records have been read since the receipt of an FL_REWIND message. FL_DELREC Delete a record VOID fl_delrec(UINT lsw, UINT msw); VOID fl_delrec(ULONG recnum) ; (conceptual) Delete record recnum (counting only records of types specified by t1vfile.typmask) by overwriting its record type with type 0. The uLonc recnum is actually passed in the message as two uINT parameters, 1sw (least significant word) and msw (most significant word). Calls p_1eave on error, in which case the record may not have been deleted. It is the user's responsibility to determine whether the record has been deleted (for example, by testing the number of records). FL_REPLACE Replace a record VOID fl_replace(UINT lsw, UINT msw, OP_TLV_REC *prec); VOID fl_replace(ULONG recnum, OP_TLV_REC *prec); (conceptual) Replace a record by deleting record recnum (counting only records of types specified by tivfile.typmask) and then appending the record specified by prec. The uLoNG recnum is actually passed in the message as two UINT parameters, 1sw (least significant word) and msw (most significant word). Deletes the record by sending itself an rL_pELETE message and, if this is successful, appends the new record by sending itself an FL_wRITE_REC message. Calls p_leave on error, in which case the original record may not have been deleted. If it has been deleted, the new record will either not have been appended or will have been marked as not being a valid record. FL_READ BY_TYPE Read record of specific type(s) INT fl_read_by_type(UINT type); Search forwards from the current file position and read into the internal buffer the first record whose type is one of those specified by the bitmask in type irrespective of the types specified by tivfile.typmask. Returns the record type of the record or, if the end of the file is reached before finding a record of a matching type, it returns E_FILE_EOF. Calls p_1eave for all other errors. This method should be used with caution. If it skips records that would normally be read (because they are included in t1ivfile.typmask) it may result in tivfile.pos containing an incorrect value. If there is any doubt, this method should always be followed by the sending of an rL_REWIND message. OLIB REFERENCE TLVDATA TLVDATA td_open td_save td_changed td_load_item td_save_item td_reset td_set_file td_set_item td_sense_item The tivpata abstract class provides the basic mechanisms for manipulating data that is stored in a series of records (each with a different record type) in a TLV file. Class definition Defined in sub-category file tlvfile.cl (generated header file tlvfile.g). CLASS tlvdata root Data which is loaded and saved to a tlvfile { ADD td_open Open file or revert to file ADD td_save Save modifications to file ADD td_changed Return TRUE if data changed since load ADD td_load_item Load an item ADD td_save_item Save an item ADD td_reset=p_dummy Clear data structures DEFER td_set_file Set the file characteristics DEFER td_set_item Set an item DEFER td_sense_item Sense an item TYPES { typedef struct { OP_TLVFILE tlvfile; File signature and mask TEXT ext[6]; Default extension } PR_TLVDATA_CHARS; } PROPERTY 1 { PR_TLVFILE *tlv; Handle to tlvfile UWORD cl_tlv; Clean id for tlv file UWORD changed; TRUE if data changed since load WORD index; Index for load and save WORD tmask; Valid record mask TEXT name [P_FNAMESIZE]; Parameter file name } } Property tlv the handle of a temporary instance of the TLvr1ue class. It should not be accessed by a subclass. cl_tlv the cleanup id for the TLvF1Lz instance, used for roll-back on error. It should not be accessed by a subclass. 8 BINARY FILE MANAGEMENT changed a flag indicating that the data has changed since a previous load or save. A subclass must set this to TRUE to signal that changed data requires saving. A subclass is not expected to set a FALSE value. index the type of the last record for which data has been sensed by the td_save_item method. It should not be accessed by a subclass. tmask the mask of valid record types. It should not be accessed by a subclass. name the full file specification of the file last used in the tad_open method. It should not be accessed by a subclass. TLVDATA methods TD_OPEN Open and read file INT td_open(TEXT *name) ; Open a TLV file, read all its records into memory, overwriting any existing data, and close it again. Sends itself a r>D_RESET message and then opens, reads and closes the file specified by name. If name is NULL, the file opened by a previous td_open Or td_save is reopened, otherwise name should point to a string containing the name of the file to open. Before opening the file, a T>_sET_FILE message is sent to determine the appropriate file characteristics and extension. If not nut, the passed name is parsed (the related name being the file extension resulting from the Tp_sET_FILE message) into the t1vdata.name buffer. An instance of TLVFILE 1s created and used to open the file and read each record in turn. As each record is read, a TD_LOAD_ITEM message is sent, to store the record's content. When all the records have been read the TLVFILE instance is destroyed (which automatically closes the file). On successful conclusion the value of tlvdata.changed iS Set tO FALSE. Returns zero if successful, or E_r1LE_nx1st if the specified file does not exist. All other errors result in p_leave being called. TD_SAVE Save data to file VOID td_save (TEXT *name) ; Write the current data to the TLV file specified by name, replacing any existing file. If name is nuLL, the file opened by a previous td_open or td_save is reopened, otherwise name should point to a string containing the name of the file to open. Before opening the file, a Tt>_sET_FILE message is sent to determine the appropriate file characteristics and extension. If not nut, the passed name is parsed (the related name being the file extension resulting from the TD_SET_FILE message) into the t1vdata.name buffer. An instance of TLVFILE 1s created and used to open the file (to replace any existing file) and write the records. The data, length and type of each record is determined by sending a Tp_savE_ITEM message. When this message returns zero, indicating that there are no further records, the TLvFILE instance is destroyed (which automatically closes the file). On successful conclusion the value of t1vdata.changed 1S set tO FALSE. Any error results in p_leave being called. & TD_CHANGED Check if changed INT td_changed (VOID); Return TRuzE if the data has been changed since a load or a save. OLIB REFERENCE TD_LOAD ITEM Process a record read from a file VOID td_load_item(INT type, UBYTE *buf, UINT len); Process the record by sending a TD_SET_ITEM message. TD_SAVE_ITEM Get a record to be saved INT td_save_item(UBYTE **pbuf, UWORD *plen); Determine the type of the next record to be saved and send a Tp_sENSE_ITEM message to sense its data. The method uses tivdata.index and tlvdata.tmask to identify the record type. Returns the record type, or zero if there are no further records to be saved. & TD_RESET Reset all data VOID td_reset (VOID) ; The supplied method does nothing. It is expected to be subclassed to perform any appropriate reset action. Deferred TLVDATA methods TD_SET FILE Set the TLV file characteristics VOID td_set_file(PR_TLVDATA_CHARS *pfc)j; Write the appropriate TLV file signature header (including the mask of valid record types) and file extension to the PR_TLVDATA_CHARs Struct pointed to by pfc. On entry, pfc->ext [0] contains the character '.' and pfc->tlvfile.fsig.offset is already set to sizeof (OP_BFILE_FSIG). All other bytes are set to zero. TD_SET_ITEM Set in-memory data for a record VOID td_set_item(INT type, VOID *buf, UINT len); Store, in memory, the data for a record of type type and of length 1en, pointed to by buf. TD_SENSE_ITEM Sense in-memory data for a record INT td_sense_item(INT type, VOID **pbuf) ; Write to *pbuf a pointer to the data for a record of type type. Returns the length of the data. SERFILE TLVDATA td_open td_save td_changed td_load_item SERFILE serial modem inkdvr serdvr file td_reset td_set_file td_set_item td_sense_item 8 BINARY FILE MANAGEMENT td_save_item The serFr1.e class provides the methods for manipulating serial port parameter data saved in a .trm TLV file. Class definition Defined in sub-category file tlvfile.cl (generated header file tivfile. g). CLASS serfile tlvdata { REPLACE td_reset Set defaults REPLACE td_set_file Set the file characteristics REPLACE td_set_item REPLACE td_sense_item Set an item Sense an item CONSTANTS TE_MASK_SERIAL 0x0001 TE_MASK_MODEM 0x0002 TE_MASK_FILE 0x0004 TY_SERFILE_SERIAL 1 OBSOLETE_SERFILE_MODEM Z TY_SERFILE_FILE 3 TY_SERFILE_NEW_MODEM 4 TY_SERFILE_SERDVR 5 TY_SERFILE_LNKDVR 6 TE_XMDM_NONE 3 TE_DIAL_PULSE 1 TE_DIAL_TONE 2 TE_MODEM_300 0x01 TE_MODEM_1200 0x02 TE_MODEM_2400 0x03 TE_MODEM_4800 0x04 TE_MODEM_9600 0x05 TE_MODEM_19200 0x06 MAX_DEVICE_NAME 10 ! TTY.AS5:A plus zero terminator MAX_MODEM_CMD 40 } OLIB REFERENCE TYPES { typedef struct { ! Serial port P_SRCHAR ch; Serial port characteristics TEXT port [P_MAXDEVNAME+2]; Serial port to use } PF_SERIAL; typedef struct { ! New Modem driver P_MDMCHR mch; UBYTE phone[25]; Phone number UBYTE auto_dial; TRUE if modem to auto dial on connection UBYTE mdmdvr [MAX_DEVICE_NAME]; Modem driver to use (if any) UBYTE mdmcmd [MAX_MODEM_CMD]; Max extra configuration for modem UBYTE spare[16]; } PF_MODEM; typedef struct { ! File transfer UWORD protocol; File transfer protocol } PF_FILE; typedef struct { ! New Serial port P_SRCHAR ch; Serial port characteristics UBYTE serdvr[MAX_DEVICE_NAME]; Serial driver to use UBYTE spare[16]; } PF_SERDVR; typedef struct { ! Link drivers UBYTE masdvr [MAX_DEVICE_NAME]; Media access driver to use UBYTE lnkdvr[MAX_DEVICE_NAME]; Link driver to use UBYTE spare[16]; } PF_LNKDVR; } PROPERTY { PF_SERIAL serial; PF_MODEM modem; PF_FILE file; PF_LNKDVR Inkdvr; PF_SERDVR serdvr; } } Property Each item of property represents the data that may be stored in a record of a serial parameter .trm file. serfile.serial the data for a serial port record, of record type Ty_SERFILE_SERIAL. serfile.modem the data for a modem driver record, of record type TY_SERFILE_NEW_MODEM. serfile.file the data for a file transfer record, of record type Ty_SERFILE_FILE. serfile.inkdvr the data for a link and media access driver record, of record type TY_SERFILE_LNKDVR. serfile.serdvr the data for an extended format serial driver record, of record type TY_SERFILE_SERDVR. 8 BINARY FILE MANAGEMENT SERFILE methods & TD_RESET Reset all data VOID td_reset (VOID); Set the serial data to its default values and then set tivdata.changed tO TRUE. Sets the property data as follows: serfile.serial ch. hand P_OBEY_XOFF | P_SEND_XOFF|P_IGN_CTS ch.frame P_DATA_8 ch.tbaud P_BAUD_9600 ch.rbaud P_BAUD_9600 ch.xon 0x11 (DC1) ch. xoff 0x13 (DC3) ch. flags P_IGNORE_PARITY port WTTY SA". serfile.modem mch. supported P_SRINQ_300|P_SRINQ_1200|P_SRINQ_2400|P_SRINQ_4800|P_SRINQ_9600|P_SR mch.baudrate INQ_19200 mch.chand P_BAUD_2400 mch.options P_OBEY_DSR|P_FAIL_DSR|P_OBEY_DCD|P_FAIL_DCD|P_OBEY_XOFF |P_SEND_XOFF P_MDM_NO_MODULATION serfile.file protocol TE_XMDM_NONE serfile.serdvr ch.hand P_IGN_CTS ch.frame P_DATA_8 ch.tbaud P_BAUD_19200 ch.rbaud P_BAUD_19200 serdvr WT AP serfile.lnkdvr masdvr "MAS:" inkdvr ®LECE* All fields not explicitly mentioned above are zero-filled. & TD_SET FILE Set the serial file characteristics VOID td_set_file(PR_TLVDATA_CHARS *pfc); Set the serial file signature, extension and record type mask in the pR_TLVDATA_cHARS Struct pointed to by pfc. The file signature is set to "TRM FILE" (with the remainder of the 16 bytes zero-filled) and the file name extension to ". TRM". The type mask is set for record types Ty_SERFILE_SERIAL, TY_SERFILE_NEW_MODEM, TY_SERFILE_FILE, TY_SERFILE_SERDVR and Ty_SERFILE_LNKDVR. & TD SET ITEM Set in-memory data for a record VOID td_set_item(INT type, VOID *buf); Set the contents one of the five items of property corresponding to the record of type type, from the contents of *buf, where type is one of Ty_SERFILE_SERIAL, TY_SERFILE_NEW_MODEM, TY_SERFILE_FILE, TY_SERFILE_SERDVR OF TY_SERFILE_LNKDVvR and buf correspondingly points to a pF_SERIAL, PF_MODEM, PF_FILE, PF_SERDVR OF PF_LNKDVR Struct. Sets tivdata.changed tO TRUE. 8 - 13 OLIB REFERENCE & TD SENSE ITEM Sense in-memory data for a record INT td_sense_item(INT type, VOID **pbuf) ; Write to *pbur the address of one of the five items of property corresponding to the record of type type, where type is one of Ty_SERFILE_SERIAL, TY_SERFILE_NEW_MODEM, TY_SERFILE_FILE, TY_SERFILE_SERDVR OF TY_SERFILE_LNKDvr. The address written to *pbuf is a corresponding pointer to a PF_SERIAL, PF_MODEM, PF_FILE, PF_SERDVR Of PF_LNKDvR Struct. Returns the length of the record data. CHAPTER 9 THE CLEANUP CLass VAROOT VAFIX VAFLAT CLEANUP nrec key rlen gran level nspc base destroy vwarrepiace va_replace | va_init destroy va_count va_copy va_compress cl_init va_delete va_reclen va_deletem cl_add va_sort va_swap va_insertm cl_remove va_key va_findisgq va_insertisq va_capacity va_prec va_pbuf cl_clean_item cl_clean_level cl_set_level va_append va_insert va_search va_compare va_reset va_test The cLeanup class supplies one of the main mechanisms by which resources may be released following an error condition. A typical use is in a case where a program allocates, say, a sequence of cells which must either exist as a whole or not at all. If, during the allocate sequence, one of the later allocations fails, the previously allocated cells must be freed. The recovery process can be simplified if each cell is placed in a cleanup list as it is allocated. Once the whole sequence of allocations is complete the items may be removed from the list. If, however, there is a failure in one of the later stages, the previously allocated cells can be freed by the sending of, for example, a single CL_CLEAN_LEVEL message. Note that the process of adding an item to the cleanup list is so arranged that the addition itself can never fail due to shortage of memory. Normally, an instance of the cLEanup class is created as a component of the application manager (see the Application Manager chapter of this manual). In this case the cleaning up of partially complete allocations is generally handled automatically whenever an error occurs and the programmer's responsibility is reduced to adding items to, and removing items from, the cleanup list at the appropriate times. Because the addition and removal of items from a cleanup list that is a component of the application manager is so common, a set of convenience functions are provided. These are described in a separate section at the end of this chapter. Precursors The reader is assumed to understand: e variable arrays of fixed length records e =the p_enter and p_leave error handling services OLIB REFERENCE Class diagram Class definition ae ae ¢ Varoot / as ) bee aon ae ( Vafix / ~ Sash Me A ee ( Vaflat / ) eee (cleanup / = ) eee Defined in sub-category file appman.cl (generated header file appman.g). CLASS cleanup vaflat { REPLACE destroy ADD ADD ADD ADD ADD ADD cl_init cl_add cl_remove cl_clean_item cl_clean_level cl_set_level CONSTANTS TYPES } typedef struct typedef struct } PROPERTY } Property { UWORD level; } cleanup.level Destroy all items in cleanup list Initialise cleanup table Add an item for cleanup Remove an item (without cleanup) Clean up an item Clean up all items at current level Set the cleanup level TY_CLEANUP_DYL -5 A dyl handle TY_CLEANUP_SHARED -4 A shared allocated cell TY_CLEANUP_VOID -3 Already cleaned up TY_CLEANUP_ALLOC -2 An allocated cell TY_CLEANUP_IOCHAN -1 An IO channel TY_CLEANUP_OBJECT 0 An object (O_DESTROY method number !!) BYTE type; Type of resource UBYTE level; Cleanup level HANDLE h; Handle of resource RC_CLEANUP; UWORD nref; SHARED_ALLOC; current cleanup level the current cleanup level; set by c1_set_1leve1 and used by elclean_level 9 THE CLEANUP CLASS CLEANUP Methods J DESTROY Destroy VOID destroy (VOID) ; Clean up all items in the cleanup list and supersend a pEsTRoy message. CL_INIT Initialise list VOID cl_init (UINT num); Initialise the cleanup list. Sends itself a va_inrT message to initialise the array for records of length sizeof (RC_CLEANUP), With a granularity of num. Then sends itself a va_capacitTy message to set the capacity to num records and ensures that the array contains at least one empty cleanup slot (of type Ty_cLEANUP_VvoID). CL_ADD Add item UINT cl_add(UINT type, HANDLE h); Add an item to the cleanup list of type type and handle h to the cleanup list at the current cleanup level. The possible values of type are: TY_CLEANUP_OBJECT h is the handle of an object, as returned by p_new TY_CLEANUP_IOCHAN h is the pointer to the channel control block, as set by p_open TY_CLEANUP_ALLOC h is the pointer to the allocated cell, as returned by p_alloc TY_CLEANUP_SHARED h is the pointer to a shared allocated cell, as returned by p_alloc (the first word of a shared allocated cell is assumed to contain a usage count) TY_CLEANUP_DYL h is the category handle of a loaded dynamic library, as set by p_loadlib All other values of type, except for Ty_cLEANUP_VvOID, are assumed to relate to objects and behave in a way similar to Ty_cLEANUP_oBJECT. In all such cases h is assumed to be the handle of the object as returned by p_new. When such an item is cleaned up, it is sent a message with message number equal to the value of type. The value ry_cLEanup_oBuectT (0) is chosen specifically to correspond to a DESTROY message. A subclasser who adds further types should respect the current scheme by using a negative number for each new type leaving positive numbers to represent object message numbers. Returns an index number which identifies the newly added item in the cleanup list. The cl_add method may call p_leave (f_GEN_NOMEMoRY), but not until after the current item has been added to the cleanup list. Thus an item cannot be 'lost' by a failure when adding it to the cleanup list. J CL_ REMOVE Remove item VOID cl_remove (UINT num); Remove the item specified by num (as returned by an earlier cL_app message) from the cleanup list without cleaning up its associated resource(s). The item is removed by setting its type to Ty_cLEANUP_vorp. No memory is freed. JCL_CLEAN ITEM Delete item VOID cl_clean_item(UINT num); Clean up the resource(s) associated with item num (as returned by an earlier cL_app message) and remove the item from the cleanup list. OLIB REFERENCE The cleanup action depends on the type of the item as follows: TY_CLEANUP_IOCHAN close the channel, using p_close TY_CLEANUP_ALLOC free the allocated cell, using p_free TY_CLEANUP_SHARED decrement the usage count of the shared allocated cell and, if decremented to zero, free the cell TY_CLEANUP_DYL unload the dynamic library, using p_unloadlib All other values are assumed to relate to an object and the object is sent a message with message number equal to the type. The special case of type Ty_cLEANUP_oBJEcT (0) corresponds to the sending of a DEsTRoY message. No allocated memory associated with the cleanup list's array is freed, but the item's type is set to TY_CLEANUP_vorn So that it is available for re-use. J CL_CLEAN LEVEL Delete all items at current level VOID cl_clean_level (VOID); Clean up all items that have been added at the current cleanup level (by sending a series of CL_CLEAN_ITEM messages). JCL_SET LEVEL Set cleanup level VOID cl_set_level(UINT level); Set the current cleanup level, stored in cleanup. level, tO level. CLEANUP convenience functions These convenience functions assume that an instance of the cLEanup class has been created during the initialisation of an instance of the application manager, and that its handle is stored in the application manager's property appman.clean. An application should ensure that the application manager's property is accessible via the 'magic static’ w_am. cl_add Add an item INT cl_add(INT type, VOID *p); Add an item, with handle p and of the specified type, to the cleanup list. The value of type may be one of: TY_CLEANUP_OBJECT TY_CLEANUP_IOCHAN TY_CLEANUP_ALLOC TY_CLEANUP_SHARED TY_CLEANUP_DYL Calling this function is equivalent to: p_send4 (w_am—->appman.clean,O_CL_ADD,type,p) ; Returns an index number which identifies the newly added item in the cleanup list. The function may call p_leave (E_GEN_NOMEMOoRY) , but not until after the item has been added to the cleanup list. cl_add_ object Add an object INT cl_add_object (VOID *p); Add an object, with handle p, to the cleanup list. Calling this function is equivalent to calling: cl_add(TY_CLEANUP_OBJECT,p) ; 9 THE CLEANUP CLASS Returns an index number which identifies the newly added item in the cleanup list. The function may call p_leave (E_GEN_NOMEMOoRY) , but not until after the item has been added to the cleanup list. cl_add_iochan Add an I/O channel INT cl_add_iochan (VOID *p); Add an I/O channel, with handle p, to the cleanup list. Calling this function is equivalent to calling: cl_add(TY_CLEANUP_IOCHAN, p) ; Returns an index number which identifies the newly added item in the cleanup list. The function may call p_leave (E_GEN_NOMEMOoRY) , but not until after the item has been added to the cleanup list. cl_add_alloc Add an allocated cell INT cl_add_alloc(VOID *p); Add an allocated heap cell, with handle p, to the cleanup list. Calling this function is equivalent to calling: cl_add (TY_CLEANUP_ALLOC, p) ; Returns an index number which identifies the newly added item in the cleanup list. The function may call p_leave (E_GEN_NOMEMOoRY) , but not until after the item has been added to the cleanup list. cl_add_shared Add a shared allocated cell INT cl_add_shared (VOID *p); Add a shared allocated heap cell, with handle p, to the cleanup list. Calling this function is equivalent to calling: cl_add(TY_CLEANUP_SHARED, p) ; Returns an index number which identifies the newly added item in the cleanup list. The function may call p_leave (E_GEN_NOMEMOoRY) , but not until after the item has been added to the cleanup list. cl_add_dyl Add a DYL INT cl_add_dyl(VOID *p); Add a DYL, with handle p, to the cleanup list. Calling this function is equivalent to calling: cl_add (TY_CLEANUP_DYL, p) ; Returns an index number which identifies the newly added item in the cleanup list. The function may call p_leave (E_GEN_NOMEMOoRY) , but not until after the item has been added to the cleanup list. OLIB REFERENCE cl_remove Remove an item VOID cl_remove (INT num); Remove the item specified by num (as returned by an earlier cL_app message or a call to one of the cl_add convenience functions) from the cleanup list without cleaning up its associated resource(s). The item is removed by setting its type to Ty_cLEANuP_vorp. No memory is freed. Calling this function is equivalent to: p_send3 (w_am—>appman.clean, O_CL_REMOVE, num) ; cl_clean_item Delete an item VOID cl_clean_item(INT num); Clean up the resource(s) associated with item num (as returned by an earlier cL_app message or a call to one of the c1_add convenience functions) and remove the item from the cleanup list. No allocated memory associated with the cleanup list's array is freed but the item's type is set to TY_CLEANUP_vorp So that it is available for re-use. Calling this function is equivalent to: p_send3 (w_am—->appman.clean, O_CL_CLEAN_ITEM, num) ; CHAPTER 10 THE APPMAN APPLICATION MANAGER CLASS APPMAN clean system rcb sxrcb ipcs task stop nrid err sparel spare2 am_init am_start am_stop am_add_task am_wait am_load_resource am_load_res_buf am_rscname am_notify am_notifyerr am_clean_up am_onlyone am_findimg am_change_pri The main function of the application manager class appman is to provide an application's central logic for scheduling the processing of events which may derive from more than one source. As such, it is fundamental to the operation of a SIBO application, providing the basic support for a multi-threaded approach to the processing of events from different sources (such as keypresses, the receipt of data from a serial port and the expiry of timers). In addition, the application manager supplies some general utilities, including methods to access resources held in resource files, together with some basic error handling and notification services. An application process almost invariably creates an instance of the application manager or, more commonly, an instance of a user interface subclass of the application manager (for example, HwImman - see the HWIM Reference manual). This instance normally remains in existence for the lifetime of the application process. The reserved static w_am is intended to be used to store the handle of an application's application manager, making its methods accessible from any part of the application code. Each of the events that are scheduled by appman is represented by an active object, i.e. an instance of a subclass of active. The application manager maintains a queue, in priority order, of instances of active objects and the am_start method schedules processing between them in a non pre-emptive way. 10-1 OLIB REFERENCE For example, an application which is printing could have the following structure: Ys where the application manager (AM) holds a queue of three active objects, WS, PR and TI, representing: e the window server (WS) e aprinter channel (e.g. a serial port) (PR) e an asynchronous timer (TI) In this example, printing is performed by making a write request on the printer active object and a time-out request on the timer active object. (Note that, in general, there will also be an outstanding event read request on the window server active object.) The application manager waits for an event which, in this case, will be the completion of any one of the requests on the three active objects. When an event occurs, the application manager scans its active object queue to determine which active object has a completed request and is prepared to run. The application manager will then send an ao_RuN message to the appropriate active object. If, in our example, the printer write request completes, the printer active object will be sent an ao_RUN message. The printer's ao_run method will typically cancel the timer's time-out request and then repeat its own write request and the timer's time-out request, to continue printing. Alternatively, if the time-out expires, the timer active object will be sent an ao_RuN message. The timer's ao_run method will abandon printing by cancelling the write request on the printer active object. A window server read event may complete at any point in the printing process - for example, to redraw a window or to indicate loss of foreground. In this case the window server active object will receive an AO_RUN message and the processing of the window server event is automatically interleaved with the processing of the write and time-out events but note that the processing of a write or a timeout event cannot be interrupted to handle a window server event. Active object priorities The application manager's queue of active objects is maintained, and scanned, in priority order. The priority is a signed value, so that the default value of zero is in the middle of the range. The range of predefined priorities is given in the Active Objects chapter of this manual. If more than one active object has generated an event, the first task in the queue is given absolute priority - a task at the end of the queue only runs when all earlier tasks are not prepared to run. It is fundamental to the scheduling process to note that: e the events which signal the completion of requests do not necessarily occur in the order in which the requests were made e the active objects are not necessarily given an opportunity to run in the order of completion of the corresponding requests - if more than one request has completed, the scheduling mechanism will give the object with the highest priority the first opportunity to run. However, each active object which makes a request will, of course, eventually receive an invitation to run at some time following the completion of its request. Once the application manager has sent an ao_RUN message to an active object, no other active object can be given an opportunity to run until the processing of the ao_RuN message is complete and the ao_run method has returned. The application manager has no means of preempting the current active object (contrast this with the EPOC operating system in which scheduling is preemptive). If the processing of an event takes an extended time to perform, all sources of events are blocked for that period of time and this may reduce the perceived quality of the application. In particular, the processing of user input (seen as an event from the window server) is delayed - the application temporarily goes deaf. A technique for coping with this situation is discussed in the Idle Objects and the AIDLE Class chapter of this manual. 10-2 10 THE APPMAN APPLICATION MANAGER CLASS Active object scheduling APPMAN'S active object scheduling is a complex process, requiring close cooperation between appman and the active objects in its queue. During the process, appman reads and modifies property elements of the active objects. It is, therefore, not possible to discuss the scheduling process without making some reference to the behaviour of active objects. Perhaps the clearest approach is to consider what qualifies an active object to be offered a chance to run. The first requirement is that it must have made a request to be run, usually by execution of its ao_queue method. At this point it will have triggered a sequence which will eventually result in an event being detected by appman. appman detects an event by calling p_iowait which returns when p_iosignal is called.! The ao_queue method will normally trigger a p_iosignal by making an asynchronous request, during which the active.stat field is usually set to —_FILE_PENDING (but see the exception discussed below, under the heading The ao_run return value). The making of a request is indicated by the active object changing state, from inactive to active (the active.isactive field changes from FaLsE to TRUE). The later completion of the asynchronous request results in active.stat being set to a value other than E_FILE_PENDING. APpPMaN detects an event by the receipt of a signal on the I/O semaphore of its process. At this point the application manager scans, in priority order, all active objects in its queue. If, in the property of an active object, active.isactive is set to TRUE, the value of active.stat is examined. If this is set to any value other than &_FILE_PENDING the active object is assumed to be prepared to consume the event and is sent an AO_RUN message. Normally, the active object confirms that it has consumed the event (signal) by returning the value RUN_ACTIVE_USED (see below, under the heading The ao_run return value, for exceptions). If an active object confirms that it has consumed the event, the application manager scheduling loop waits for the next event, otherwise it continues looking for an active object that can consume the event. The fatal condition, known as stray signal death, occurs if the application manager reaches the end of its list before any active object consumes the event. In this situation the application manager calls p_panic (P_PANIC_APPMAN_1). (This panic has the value 143.) (For further details of asynchronous processes, see the Asynchronous Requests and Semaphores chapter in the PLIB Reference manual.) Note that appman reads an active object's active.isactive and active.stat property fields for reasons of efficiency. It avoids the duplication of the tests of these fields in the ao_run method of each active object and, more importantly, executes more efficiently since messages are not sent to active objects that are not prepared to run. The ao_run return value Normally, an active object will represent a source of events of a single type. According to the above description of the scheduling mechanism, the active object will not be sent an ao_Run message unless the corresponding asynchronous event has completed. In consequence, the ac_run method of such an active object can only ever return the value RUN_ACTIVE_USED. For largely historical reasons an ao_run method may return RUN_ACTIVE_UNUSED to indicate that it has not consumed the event. This could, for example, be of use where a single active object is used to represent two or more related event sources of different types, for example, serial port reads and writes. Such an active object would need to maintain a separate status word (in its property) for each type of asynchronous request, leaving active.stat with a permanent zero value. It would then be liable to receive an AO_RUN message at any time that active.isactive iS TRUE, regardless of the completion status of any of its outstanding asynchronous requests. The ao_run method should only return RUN_ACTIVE_UNUSED if none of its outstanding requests have completed. This technique, although relatively simple to implement, is inefficient if the application contains other active objects of equal or lower priority. In such a situation the active object will, in general, be sent a number of ‘unnecessary’ Ao_RUN messages. From an architectural point of view, and in the interests of efficient execution, it is better to implement the handling of multiple event sources by using a separate active object for each event source. Each active object will then only be sent an ao_RuN message when its corresponding outstanding request has completed (and will always return the value RUN_ACTIVE_USED). ! The call to p_iosignal is thus the event source. 10-3 OLIB REFERENCE Precursors The reader is assumed to understand: e the PLIB/EPOC I/O system, waits, signals and semaphores. e the requirements of an event-driven system. e =the p_enter and p_leave error handling services. Class diagram “— / — ¢ appman ¢ cleanup =. x ) 7 system / kg. gre 5 Sas es = ae (oa Se Se y tscfile / ¢ pes / ~~ ) S _) i oe ae APPMAN may optionally reference (use) one or more of the CLEANUP, RSCFILE, IPcs and system classes. Class definition Defined in sub-category file appman.cl (generated header file appman.g). CLASS appman = root Application manager -— schedules attached active objects { ADD am_init Initialise task queue ADD am_start Start a scheduling loop ADD am_stop Exit one level of the scheduling loop ADD am_add_task Insert active object into task queue ADD am_wait=p_iowait Wait for the next signal ADD am_load_resource Load a resource file record into memory ADD am_load_res_buf Load a resource file record into buf supplied ADD am_rscname Supply a resource file name at initialisation ADD am_notify Notify user ADD am_notifyerr Notify user of error ADD am_clean_up Clean all logged objects then do an abrun ADD am_onlyone Called when the only one check fails ADD am_findimg Re-find the image if the pack is moved ADD am_change_pri Change the priority of an active object CONSTANTS { FLG_APPMAN_CLEAN Ox01 Create a cleanup list component FLG_APPMAN_ SYSTEM 0x02 Create a system configuration component FLG_APPMAN_RSCFILE 0x04 Create a resource file component FLG_APPMAN_SRSCFILE 0x08 Create a system resource file component FLG_APPMAN_IPCS Ox10 Create an ipcs component FLG_APPMAN_ONLYONE 0x20 Fail if same process already exists FLG_APPMAN_NODBG 0x40 Don't grope for dbg.dyl if set RUN_ACTIVE_UNUSED 0 Signal not used RUN_ACTIVE_USED 1 Signal used ERR_APPMAN_APPL =512 Base for application specific leaves } PROPERTY 5 { PR_CLEANUP *clean; cleanup list for leaves PR_SYSTEM *system; system configuration object PR_RSCFILE *rcb; application resource file PR_RSCFILE *srcb; system resource file PR_IPCS *ipcs; ipcs object P_QUE task; queue header UWORD stop; start level counter WORD nrid; context message rid for notify WORD err; abrun error UBYTE *sparel; Spare for future expansion. UBYTE *spare2; Spare for future expansion. } 10-4 Property appman. appman. appman. appman. appman. appman. appman. appman. appman. appman. appman. clean system rcb sxrcb ipcs task stop nrid err sparel spare2 10 THE APPMAN APPLICATION MANAGER CLASS Contains the handle of the created cLzanup object if the FLG_APPMAN_CLEAN flag was specified to am_init. The cLzanup object handle is used when an active object's ac_run method leaves with error. All applications will normally create a cLEanup object. This field should be treated as read only by all objects. Contains the handle of the created system configuration object if the FLG_APPMAN_SYSTEM flag was specified to am_init. This field should be treated as read only by all objects. Contains the handle of the created application resource file Rscr ILE object if the FLG_APPMAN_RSCFILE flag was specified to am_init. This object is used in the am_load_resource and am_load_res_buf methods, provided the specified resource id is positive. This field should be treated as read only by all objects. Note that rscriLe objects require the use of the application manager's cLEANUP object. Contains the handle of the created system resource file Rscr1LE object if the FLG_APPMAN_SRSCFILE flag was specified to am_init. This object is used in am_load_resource and am_load_res_buf methods if the specified resource id is negative. This field should be treated as read only by all objects. Note that rscrILE objects require the use of the application manager's CLEANUP object. Contains the handle of the created 1pcs object if the rLG_APPMAN_IPCS flag was specified to am_init. This field should be treated as read only by all objects. This is the head of the active object task queue. All active objects are inserted into this queue when they send the application manager an AM_ADD_TASK message. The queue is maintained in priority order. Note that if an active object wishes to change its priority it should send appman an AM_CHANGE_PRI message; just changing the priority property field is not sufficient. This field should not be accessed by any subclass. Maintains the current level of active object event scheduling, as set by the am_start and am_stop methods. This field should not be accessed by any subclass. This field is intended to be used by applications to store a resource id to be used in reporting errors. The idea is that this resource id changes as the execution of code progresses, the id providing a context of where the error(s) are occurring. appMan makes no use of this variable itself, but it is used by ACTIVE’s ao_abrun method. Contains the error returned by the ao_run method of an object. The error number is placed there by the am_clean_up code before the ao_abrun method is called. This allows the error to be more accessible than if it were just passed as a parameter, and also reduces stack build up. Reserved for future expansion. APPMAN Methods AM_INIT VOID am_init (UINT flags); Initialise Initialise the task queue and create a series of objects as determined by flags which should contain a combination of the flag values listed below. If an error is encountered during initialisation p_1leave is called with the appropriate error. If the initialisation fails, an application must assume that none of the requested objects have been created. In particular, no resource files will have been opened and hence the application can only exit, without attempting to use any resource data. 10-5 OLIB REFERENCE The following descriptions of the various flag values make reference to a number of other object classes. For more information on any of these classes, see the appropriate section of this manual. FLG_APPMAN_CLEAN FLG_APPMAN_SYSTEM FLG_APPMAN_RSCFILE FLG_APPMAN_SRSCFILE FLG_APPMAN_IPCS FLG_APPMAN_ONLYONE FLG_APPMAN_NODBG © AM_WAIT VOID am_wait (VOID) This flag causes an instance of the cLEanup class to be created and initialised with a granularity of 8. This is used to lodge items that must be 'cleaned up' on error. This flag causes an instance of the system class to be created and initialised (see the System Services chapter). This is used to obtain system-wide information. This flag causes an instance of the rscFILE class to be created to provide access to the application resource file (see the Resource Files chapter). The name of the resource file is generated by the am_rscname method, which should be subclassed if a different name is required. Note that the rscriLE class requires the presence of an instance of the cLzanup class, so this flag must always be accompanied by rLG_APPMAN_CLEAN. This flag causes an instance of the rscFrILz class to be created, to provide access to the system resource file (see the Resource Files chapter). Note that the rscri1Le class requires the presence of an instance of the cLEanup class, so this flag must always be accompanied by rLG_APPMAN_CLEAN. This flag causes an instance of the 1pcs class to be created and initialised with a maximum message size of 8 bytes, in a queue of length 4 (see the Inter-process Communication chapter). If this is insufficient then the application should not use this flag, but should explicitly create its own 1pcs object. This flag should be set if there must be only one process of this type running at any one time. It is, for example, set for Alarms, Link and the System process. If this flag is set and another process exists with the same name as the one now being run, appman sends itself an am_ONLYONE message, passing the process id of the other process. In the absence of Psion's internal test and debug library, dbg.dyl, this flag has no effect. If this flag is clear and dbg.dy1 is present in the appropriate directory, a debug object is created to run test procedures and display various items of debug information for the application. Wait on I/O semaphore Waits on the I/O semaphore by calling p_iowait. When an event occurs, it signals the I/O semaphore which causes p_iowait, and hence am_wait, to return. AM_START VOID am_start (VOID) Start scheduler Start a new level of the application manager active object event scheduler. This method normally does not return until the application manager receives an AM_STOP message. The code of this method is presented below, since it is crucial to the understanding of the application manager's active object scheduling mechanism. 10 - 6 10 THE APPMAN APPLICATION MANAGER CLASS LOCAL_C RunTask(FAST PR_ACTIVE *htask) { INT ret; htask->active.isactive=FALSE; ret=p_send2 (htask,O_AO_RUN) ; if (ret==RUN_ACTIVE_UNUSED) htask->active.isactive=TRUE; return (ret); } LOCAL_C RunCleanupAbrun(PR_APPMAN *self,INT err,VOID *htask) /* Enterable shell e/. { p_send4 (self,O_AM_CLEAN_UP,err,htask) ; return (0); } METHOD VOID appman_am_start (PR_APPMAN *self) /* Start the basic loop to get a message from the server. May be called recursively for modal interaction. %/ { INT stop, ret, abret; FAST P_QUE *pt; FAST PR_ACTIVE *htask; stop=(++self-—>appman.stop); if (self->appman.clean) p_send3 (self-—>appman.clean, O_CL_SET_LEVEL, stop) ; do { p_send2(self,O_AM WAIT); /* wait for next event */ for (pt=self->appman.task.next;;pt=pt—>next) { /* find a task to run */ if (pt==&self—>appman.task) p_panic(P_PANIC_P_APPMAN_1); /* stray signal */ htask=(PR_ACTIVE *) (((UBYTE *)pt)-sizeof(PR_ROOT) ); if (htask->active.isactive && htask->active.stat!=E_FILE_PENDING) { abret=0; if ((ret=p_enter2 (RunTask,htask))<0) /* p_leave(err) called */ { abret=p_enter4 (RunCleanupAbrun, self, ret, htask) ; if (abret) self-—>appman.stop-—; } if (ret !=RUN_ACTIVE_UNUSED) break; } } while (self-—>appman.stop==stop) ; if (self->appman.clean) { p_send2 (self-—>appman.clean, O_CL_CLEAN_LEVEL) ; p_send3 (self-—>appman.clean, O_CL_SET_LEVEL, stop-1) ; } if (abret) p_leave(abret); /* allow +ve 'errors' */ } On entry, appman. stop is incremented. Provided appman.clean is non-zero, the cLEANUP object is sent a CL_SET_LEVEL message to set its level to the new value of appman. stop. The scheduler waits for an event by sending itself an am_wart message. On return, all objects that are currently active (active.isactive Set to TRUE) have their completion status words (active.stat) checked. If the completion status word is not E_FILE_PENDING the object is sent an ao_RUN message. 10-7 OLIB REFERENCE Assuming that there are no errors, if the object does not consume the event it must return RUN_ACTIVE_UNUSED, Otherwise it returns any other value, normally returning RUN_ACTIVE_USED. If the list of active objects is exhausted without the event being consumed, the scheduling loop calls p_panic (P_PANIC_APPMAN_1), indicating the stray signal death condition. Before sending the ao_run message, the application manager sets the active object's active.isactive to FALSE. If the object returns RUN_ACTIVE_UNUSED, active.isactive Is set back to TRUE since the object must still be left in the active state if it does not consume the event. If the active object leaves with an error, as described below, the TRuz value is not written to active.isactive. (Note that changing the value of active.isactive is a code-saving service, avoiding the duplication of code in each active object.) Any error in the ao_run method is expected to result in p_leave being called. The ao_Run message is sent under the protection of a p_enter which catches any p_leave error calls. If an active object calls p_1eave within its ac_run method the application manager will receive an error (negative) return value and will then send an am_cLEAN_UP message, passing the error that was detected and the handle of the active object that called p_ieave. The am_cLEAN_upP message is also sent under the protection of a p_enter and any non-zero return value (representing a p_leave exception in the error handling code) is stored for later use. Note that this will cause the current level of event scheduling to terminate, with the error being propagated to the previous level. The technique of calling p_1eave within the error handling code should therefore only be used with extreme caution. Note that an ao_run method is free to terminate its processing prematurely by calling p_leave with a zero or positive argument - a preferred form of the call is p_leave (RUN_ACTIVE_USED). Such termination will not trigger the error reporting and recovery mechanism and may be considered equivalent to a normal termination that returns RUN_ACTIVE_USED. Once an object consumes the signal, by returning a value other than RUN_ACTIVE_UNUSED from its ao_run method, no more objects are polled. At this point the am_start method normally loops back to send itself another am_wAIT message to wait for the next event. The exceptions to this are: e if an active object has sent an aM_sTop message in its ao_run method e if a non-zero return value resulted from the am_cLEAN_UP message. In either case appman. stop will have been decremented. On completion of the processing of the current event, the am_sTart method returns, exiting one level of scheduling. Before returning, the application manager's cLEaNuP object (if it exists) is sent a CL_CLEAN_LEVEL message, to discard all items still in the cleanup list at the current level. It is then sent a cL_SET_LEVEL message to adjust it to the new (lower) scheduling level. If a non-zero value resulted from the AM_CLEAN_UP message, p_leave Is called, passing this value, to propagate the exception generated in the error handling code to the previous level of scheduling. The application manager active object event scheduler is re-entrant. Thus the ao_run method of an active object can send the application manager an aM_sTART message to enter a further level of scheduling. Normally, the active object which sends the am_start message will, at that time, have active.isactive set to FALSE (by the application manager, before it sends the ao_RuN message) and will therefore not receive any further ao_ruN messages until an amM_sTop message is sent. Events occurring under other active objects in the application manager's queue will, however, continue to be processed as normal. An important example of such use is when a modal dialog box is being run from a menu selection. There is a great temptation to use this technique to implement any synchronous sequence of actions by means of an active object whose initialisation method, say, sends an ao_QuEUE message and then sends the application manager an am_sTart message. This technique should be used with care, particularly when recovery from an error involves items on the cleanup list. Because of the different level, the items that are cleaned will be different for an error that occurs before an AM_START message (for example, during an initialisation phase) from the items that are cleaned up after (say, within an ao_run method). It may be advisable to transfer any part of the initialisation that can fail into an ao_run method and execute it under the control of a state variable in the object's property, the first time that the ao_run method is called. This also resolves any problem as to whether the error recovery code should or should not send an am_stop message. The following general code briefly illustrates the principle of implementing such an active object sequencer: 10-8 10 THE APPMAN APPLICATION MANAGER CLASS GLREF_D PR_APPMAN *w_am; VOID sequence_ao_init (PR_SEQUENCE *self) { self—>active.priority=PRIORITY_ACTIVE_COMPUTE; p_send3 (w_am, O_AM_ADD_TASK, self) ; self—>active.isactive=TRUE; p_iosignal(); p_send2 (w_am,O_AM_START) ; } sequence_ao_run(PR_SEQUENCE *self) { switch (self-—>sequence.state_variable) { case 0: /* initialise */ break; case 1: /* action 1 */ break; case 2: case 5: p_send2 (w_am,O_AM_STOP) ; return (RUN_ACTIVE_USED) ; } self—>sequence.state_variablet=1; self—->active.isactive=TRUE; p_iosignal(); return (RUN_ACTIVE_USED) ; } & AM _STOP Stop scheduler VOID am_stop (VOID) Stop the current level of the active object event scheduling loop, by decrementing appman. stop. This causes the most nested am_start to return after handling of the current event is complete. & AM_ADD_TASK Add a task VOID am_add_task(PR_ACTIVE *hand) ; Add an initialised active object to the task queue, in priority order, as determined by the active object's active.priority field. The item is added to the list immediately following all existing items with the same (or higher) priority. AM_LOAD_ RESOURCE Load a resource INT am_load_resource(INT resid, UBYTE **ppdata) ; Allocate a buffer and load into it the resource with id resia from the appropriate resource file. A negative resid indicates that the resource is to be found in the system resource file, with an id equal to the absolute value of resid. Otherwise it is assumed that the resource is to be found in the application resource file. If you wish to access the system resource file you must have passed the FLG_APPMAN_SRSCFILE flag to the am_init method. It is recommended, but not strictly essential (because the am_load_resource method will search for and open the application resource file if it is not already open) that you pass the FLG_APPMAN_RSCFILE flag to the am_init method if you wish to access the application resource file. If the appropriate rscFILE object exists, the resource is loaded by sending an rs_READ message to the appropriate RSCFILE object, under the protection of p_enter. If the loading of the resource fails due to out of memory then p_leave (E_GEN_NoMEmoRY) is called. 10-9 OLIB REFERENCE Any other error when attempting to read an application resource, including the absence of the application resource RSCFILE object, is assumed to be due to the removal of the application resource file. (It is assumed that only the application resource file can be removed since the system resource file is in the ROM.) If the application manager's application RscrILE component exists, it is destroyed. An attempt is then made to locate the resource file by sending am_Finp1mc and am_RscnameE messages. If this is successful the RscFILE object is recreated and the file reopened - either of which could fail, calling p_leave (E_GEN_NOMEMoRY) - otherwise the method calls p_1eave (Z_FILE_NXIST) . Following this, the resource is loaded by sending the appropriate rscrILz object an RS_READ message, which may fail - typically by calling p_leave (E_GEN_NOMEMORY) . The method returns the size of the loaded resource, as returned by the rs_READ message. AM_LOAD_RES_ BUF Load a resource to a buffer INT am_load_res_buf (INT resid, UBYTE *pbuf) ; Load into the buffer pointed to by pbut the resource with id resia from the appropriate resource file. A negative resid indicates that the resource is to be found in the system resource file, with an id equal to the absolute value of resid. Otherwise it is assumed that the resource is to be found in the application resource file. If you wish to access the system resource file you must have passed the FLG_APPMAN_SRSCFILE flag to the am_init method. It is recommended, but not strictly essential (because the am_load_resource method will search for and open the application resource file if it is not already open) that you pass the FLG_APPMAN_RSCFILE flag to the am_init method if you wish to access the application resource file. If the appropriate RscFILE object exists, the resource is loaded by sending an rs_READ message to the appropriate RSCFILE object, under the protection of p_enter. If the loading of the resource fails due to out of memory then p_leave (E_GEN_NoMEMoRY) is called. Any other error when attempting to read an application resource, including the absence of the application resource RSCFILE object, is assumed to be due to the removal of the application resource file. (It is assumed that only the application resource file can be removed since the system resource file is in the ROM.) If the application manager's application RscrILE component exists, it is destroyed. An attempt is then made to locate the resource file by sending am_F1npIMc and am_RscnamE messages. If this is successful the RscFILE object is recreated and the file reopened - either of which could fail, calling p_leave (E_GEN_NOMEMORY) - otherwise the method calls p_1leave (Z_FILE_NXIST) . Following this, the resource is loaded by sending the appropriate rscFrILz object an RS_READ message, which may fail - typically by calling p_leave (E_GEN_NOMEMORY) . The method returns the size of the loaded resource, as returned by the rs_READ_BUF Message. AM_RSCNAME Generate resource file name VOID am_rscname(UBYTE *pname) ; Write to the buffer at *pname (which must be at least p_rnames1zeE bytes long) the default full file specification (see the Files chapter of the PLIB Reference manual) of the application resource file. The name is generated from the application's start-up full file specification, pointed to by the magic static DatCommandPtr. The resource file is assumed to be built into the image file, so that the full file specification is identical to that of the image file. & AM_NOTIFY Display notifier VOID am_notify(UINT messl, UINT mess2, UWORD *pbut) ; Call the p_notify service with text loaded from resource files. Up to two text messages are specified by the resource ids mess1 and mess2. If pbut is Nutt, the single default button 'CONTINUE' (or the non-English equivalent) will be displayed. Otherwise, pbut is assumed to point to an array of three resource ids for the three notifier buttons. All resource ids follow the resource id rules as specified in the description of the am_load_resource method. If any id is nunz then no text is loaded for that id. 10 - 10 10 THE APPMAN APPLICATION MANAGER CLASS Once the resource strings are loaded the p_not ify service is invoked. The allocated space for the resource strings is freed after use. This method will not fail due to lack of memory. If there is not enough memory available to load any of the specified resources, the corresponding part of the notification text is not displayed. & AM_NOTIFYERR Notify an error VOID am_notifyerr(INT err, UINT messl1,UWORD *pbut) ; Call the p_notifyerr service, with text loaded from resource files. A first line text message is specified by the resource id messi. A second line contains a description of the error, as generated by p_errs (err). If pbut is nuLt, the single default button 'CONTINUE' (or the non- English equivalent) will be displayed. Otherwise, pbut is assumed to point to an array of three resource ids for the three notifier buttons. All resource ids follow the resource id rules as specified in the description of the am_load_resource method. If any id is nunz then no text is loaded for that id. Once the resource strings are loaded the p_notifyerr service is invoked, which converts the error number err into the second line text message. The allocated space for the resource strings is freed after use. This method will not fail due to lack of memory. If there is not enough memory available to load any of the specified resources, the corresponding part of the notification text is not displayed. AM_CLEAN_UP Clean up resources and report an error VOID am_clean_up(INT err, UBYTE *htask); Provide standard error recovery and reporting for the active object event scheduler. This method is called from the am_start event scheduler if an active object's ac_run method calls p_leave (error). The handle of the active object is in htask and err is the error number passed to p_leave. The value of err is copied to appman.err and if there is a cLEaNupP object it is sent a CL_CLEAN_LEVEL message to tidy up all resources added to the cleanup list at this level of event scheduling. If htask is not NULL, AN AO_ABRUN message Is sent to that object. The ao_abrun method may call p_leave, in which case the error will be caught in the am_start method. It will cause the current level event scheduling to terminate, the p_leave error being propagated to the next level of scheduling. This method is supplied in order to facilitate the customising of all, or a particular set of, errors. Note that the active object which generated the error in its ao_run method is sent an Ao_ABRUN message after the sending of the cL_cLEAN_LEVEL message. This means that (unless the am_cleanup method is subclassed) the active object must not itself be in the cleanup list at the current level, otherwise it will be destroyed before the ao_aBrun message is sent. It is likely, in any practical case, that an active object that has been placed in the cleanup list will have been removed before it receives its first ao_RUN Message. In general, the active object will only be placed in the cleanup list temporarily while other objects are being built and resources acquired; in this state of construction, it is unlikely that an application would "activate" the active object and risk receiving an Ao_ABRUN Message. AM_FINDIMG Find application image file INT am_findimg (VOID) ; Relocate the SSD from which the application was run. It uses the magic static DatCommandPtr, assuming that it points to the current full file specification of the application's .img (or .app) file. It looks in all available SSD drives (A and B and, if they exist, C and D). If it finds a file with the same name in the directory specified by pat commandPtr it patches the data at DatCommandPtr to reflect the new path and returns zero. If no such file can be found £_F1LE_nxist (the return value from a p_finfo call) is returned. Typically this is called when the application wishes to access some information from the SSD from which it was run, but finds that the SSD is no longer in that drive. It is used in this way by the am_load_resource and am_load_res_buf methods. 10-11 OLIB REFERENCE AM_ONLYONE Ensure only one copy running VOID am_onlyone(UINT pid); Ensure that a second copy of an application is not launched. This method simply calls p_leave (E_FILE_EXIST). It is called during the am_init method if the rLc_appMaN_oNLyYonE flag was specified and another process of the same name is already running. The Alarm application is an example of a process which should never have more than one copy running. & AM_CHANGE_PRI Change active object priority VOID am_change_pri(PR_ACTIVE *pObject, INT priority); Change the priority of the active object with handle pobject to the value in priority. The result will be unpredictable if the object is not currently in the application manager's active object queue. The active object is removed from the application manager's active object queue, the new priority is copied into active.priority and the object is then re-inserted with the new priority. 10-12 CHAPTER 11 THE ACTIVE CLASS AND ACTIVE OBJECTS ACTIVE gq priority isactive pcb stat destroy ao_init ao_cancel ao_abrun ao_queue ao_run The active class provides common behaviour for active objects. An active object may be thought of as an event source and is, by definition, any object which has acTIVE as an ancestor in its inheritance tree. Active objects are fundamental to the operation of event-driven SIBO applications (the overwhelming majority of all SIBO applications). In such an application, virtually all processing is performed within the ao_run method of some active object or other. Although it is not formally an abstract class, the acTIveE class must be subclassed to create a useful active object. OLIB and HWIM supply a number of subclasses of active for use by applications. In addition, an application may define one or more application-specific active object classes. A typical active object corresponds to an asynchronous channel on one of PLIB's I/O devices. An assumption, embodied in acTIve's property, is that only one asynchronous event per active object can be outstanding at any one time. See the Application Manager chapter for further information about active objects and event scheduling. Note that active objects making asynchronous requests on the file server should subclass FAcTIVE in preference to active. See the File Active Objects chapter for further details. Precursors The reader is assumed to understand: e the appman class, in particular the event scheduling mechanisms. e the PLIB/EPOC asynchronous I/O system, waits, signals and semaphores. e requirements of an event-driven system. e =the p_enter and p_leave error handling services. 11-1 OLIB REFERENCE Class definition The actrve class subclasses root and is defined in the sub-category file appman.cl (with generated header file appman.g). CLASS active root Active object superclass for representing event sources { REPLACE destroy Close IO channel and free itself ADD ao_init Open IO channel ADD ao_cancel Cancel outstanding read request ADD ao_abrun Abnormal, p_leave induced, termination of ao_run ADD ao_queue Queue request (normally subclassed) ADD ao_run Provide an opportunity to run CONSTANTS { ! Priorities PRIORITY_ACTIVE_POSTER 100 PRIORITY_ACTIVE_IPCS 80 PRIORITY_ACTIVE_VOICE 70 PRIORITY_ACTIVE_WSERV 60 PRIORITY_ACTIVE_COMMAND 40 PRIORITY_ACTIVE_SERIAL 20 PRIORITY_ACTIVE_ALARM 0 PRIORITY_ACTIVE_FILES -20 PRIORITY_ACTIVE_REPEATER -40 PRIORITY_ACTIVE_PRINT -60 PRIORITY_ACTIVE_COMPUTE -100 } PROPERTY { P_QUE q; queue header BYTE priority; priority compared to other active objects UBYTE isactive; TRUE if there is a request pending UBYTE *pcb; I/O channel WORD stat; I/O completion status } } Property active.g Used by the application manager to include an active object in its prioritised queue. It should not be accessed other than by the application manager and an active object's destroy method. active.priority Used to determine the object's position in the application manager's prioritised queue. The value, which will normally be one of the priorities listed in the acttve class definition, should be set up prior to sending the application manager an aM_ADD_TASK message. active.isactive Should be set to TRuE when the active object is (or will be, on completion of an outstanding asynchronous request - see also active.stat) prepared to receive an Ao_RUN message. A common error is to fail to set active.isactive to TRUE when an asynchronous request is made. This will eventually cause stray signal death, described in the Application Manager chapter of this manual. The application manager sets active.isactive to FALSE when it sends the ao_RUN message, avoiding the need for the ao_run method of each individual active object to clear this field. active.pcb Normally holds the handle of the device upon which the active object makes its I/O requests. Many of the methods supplied by the actrve class assume that this is a true I/O channel handle. active.stat Normally used as the completion status word for an asynchronous request. Only if its value is not =_FILE_PENDING will the application manager's event scheduling loop send an ao_Run message to this active object (active.isactive must also be TRUE). 11-2 11 THE ACTIVE CLASSS AND ACTIVE OBJECTS ACTIVE methods J DESTROY Destroy the instance VOID destroy (VOID) Takes the following actions: e sends itself an ao_caNcEL message to cancel any pending asynchronous request e removes itself, if necessary, from the application manager's task queue e closes any I/O channel, whose handle is assumed to be in active.pcb (this is harmless if active.pcb is NULL) e supersends itself a pEsTRoy message. If a subclass uses active.pcb to contain anything other than an I/O channel handle, it should ensure that active.pcb 1s set to nuLL before this method is executed. AO_INIT Initialise the instance VOID ao_init (TEXT *devname, INT mode) ; Initialise the active object. Uses p_open to open a channel to the device specified by devname and mode, writing the channel handle to active.pcb. Calls p_leave if there is an error opening the channel. The object is not added to the application manager's task queue and no other fields in the property are changed. J AO_QUEUE Make a request to run VOID ao_queue (VOID) ; Set active.isactive to TRUE and signal the I/O semaphore by calling p_iosigna1 without altering the value of active.stat (whose default value is zero). As a result, the object will eventually be sent an AO_RUN message by the application manager. This method is provided for use by idle object subclasses (see Idle Objects and the AIDLE Class). Other active objects would normally subclass this method. All subclasses must ensure that the ao_queue method sets active.isactive tO TRUE. J AO CANCEL Cancel a request to run VOID ao_cancel (VOID) Cancel any outstanding asynchronous request. Does nothing if active.isactive is not TRUE. If active.isactive iS TRUE then it is re-set to FALSE. If active.pcb 1S not NULL, it is assumed to be the handle of an I/O channel and a p_FcancEL request is made to that channel. Regardless of the value of active.pcb, this is followed by a p_waitstat, waiting on active.stat. The ao_caNcEL message may be received before or after the corresponding request has completed: e if it is before the completion, the outstanding request is cancelled and the p_waitstat waits for, and absorbs the event which signals the completion of the cancel e if itis after the completion (but before its processing) the p_rcancEL request does not generate its own completion event. In this case the p_waitstat consumes the event already generated by the completion of the asynchronous request, effectively discarding it. Note that the completion result placed in active.stat 1s likely to be different in the above two cases, but is normally ignored since the status following a cancel is generally not significant. 11-3 OLIB REFERENCE Since the destroy method sends an ao_caNncEL message, the active class contains implicit assumptions that: e = the ao_cance1 method will never call p_leave ¢ itis safe to send an ao_caNcEL message at any time, even if there has not been a previous AO_QUEUE Message. These assumptions about the cancel service are certainly true at the PLIB level, where a p_rcanceL does not rely on there being an outstanding request (for example P_FREAD or P_FWRITE). They are also true for all system-supplied subclasses of active. Subclassers of the active class should ensure that this assumption remains true. Active objects that perform operations on files should subclass ractive (described in the File Active Objects chapter) which provides the correct support for a file system cancel service. The ao_cance1 method provided by active may safely be used by non-I/O subclasses provided they leave active.stat at NULL. AO_ABRUN Handle an error VOID ao_abrun (VOID); Report an error condition arising from a call to p_leave in the object's ao_run method. Sends the application manager an aM_NOTIFYERR message: p_send5 (w_am, O_AM_NOTIFYERR, w_am->appman.err,w_am—>appman.nrid, NULL) ; and then sets appman.nrid tO NULL. The application manager has previously set appman.err to contain the error number passed as the parameter to p_leave, and appman.nrid 1s assumed to be either nut, or an application-specific resource id. The application manager and its property are accessed via the magic static w_am, which is assumed to have been initialised (as it is, for example, in the am_init method of the swimman subclass of appman - see the HWIM Reference manual). The ao_aprun method may be subclassed to provide more specific error handling. Note that a CL_CLEAN_LEVEL message will have been sent to any application manager cLEaNnup object before the AO_ABRUN message is received. AO_RUN Process an event INT ao_run (VOID) A default method which simply returns RuN_ACTIVE_USED, to signal to the application manager that it has consumed an event. Most active objects will subclass this method. An active object will only receive an ao_Run message if active.isactive iS TRUE and active.stat is not E_FILE_PENDING. The application manager sets active.isactive to FALSE before sending the ao_RuUN message. 11-4 CHAPTER 12 IDLE OBJECTS AND THE AIDLE CLASS Idle objects Well-behaved applications should break down long, computationally intensive operations into a sequence of smaller sections processed in idle time, so that the application can avoid going deaf to window server messages for long periods of time. In other words, these operations should only be allowed to run when no other higher priority work is ready to run (e.g. responding to window server messages). This is normally achieved by using an active object of low priority (usually PRIORITY_ACTIVE_COMPUTE) which will only be run when no other active objects have any work to perform. An active object used for such a purpose is known as an idle object. An idle object will typically leave active.stat at a zero value and use the default ac_queue method provided by the active class. Its ao_run method is usually subclassed to perform a unit of processing and then (provided processing is not yet complete) send itself an ao_QUEUE message. A partially complete operation may be invalidated by a subsequent event. In such a case the operation should be cancelled and restarted. Such use of an idle object is appropriate, for example: in a text processor word wrapping a line at a time without falling behind in echoing user input in the current line. in a spreadsheet calculating a cell at a time in auto-calculate mode, allowing the user to continue to input during the calculation. An empty idle object - one whose ao_run method does nothing but send itself an ao_QUEVE message - will execute its ao_run method several hundred times per second. AIDLE ACTIVE gq priority isactive pcb stat destroy in ao_cancel ao_abrun ao_queue aeTFuFn The arpte class provides the basic functionality of an idle object. 12-1 OLIB REFERENCE The supplied ac_run method simply requests the application manager to exit from one level of application manager event scheduling by sending the application manager an am_stop message. In this form it may be used to pause some operation to allow the processing of other events. This usage, which is illustrated in the first example below, can be considered as a means of adding some aspects of idle time processing to code which, for one reason or another, is not suitable for implementation as an active object. A true idle object must subclass the ao_run method to perform the required processing, as described above, and as illustrated in the second example. Precursors The reader is assumed to understand: e =the active class e the application manager's event scheduling mechanism Class diagram fe (active / = ) CoS — — / aidle / ie ) Nee Class definition Defined in sub-category file appman.cl (generated header file appman.g). CLASS aidle active Idle active object. { REPLACE ao_init Add itself to active list at low priority REPLACE ao_run Send w_am an O_AM_STOP } Property None. AIDLE methods J AO_INIT Initialise VOID ao_init (VOID) Set active.priority tO PRIORITY_ACTIVE_COMPUTE and send the application manager an aM_ADD_TASK message. J AO RUN Run INT ao_run(VOID) ; Send the application manager an am_stop message and return RUN_ACTIVE_USED. 12-2 12 IDLE OBJECTS AND THE AIDLE CLASS Ds A nnn _______yz. Examples Pause an operation Some operations are not suitable for implementation in an active object format. The quicksort algorithm, for example, is recursive and therefore dependent on stacked state information. Since it could take an extended time to sort the data, some action should be taken to ensure that the calling application remains responsive to redraws and user input. A considerable rewrite would, however, be necessary to enable quicksort to execute as a sequence of separate calls to an ao_run method. A more convenient solution in such a case is to insert, at some point which is repeatedly executed, code which pauses execution and allows other events (such as redraws) to be processed. The principle is illustrated in the following code: VOID MakeIdle (VOID) { INT i; VOID *aidle; aidle=f_newsend (CAT_DEMO_OLIB, C_AIDLE, O_AO_INIT); for (i=0;i<1000;i++) { p_send2 (aidle, 0O_AO_QUEUE) ; p_send2 (w_am,O_AM_START); /* does not return until AIDLE has run */ } p_send2 (aidle,O_DESTROY) ; } The ao_quruz message is handled at the active level, simply setting active.isactive to TRUE and signalling the I/O semaphore. The send of the am_start message will not return until the am_stop message is sent by arpLE's ao_run method (which will not run until there are no outstanding events for any active object of priority higher than that of arp1z). Depending on the nature of the process, it may be more appropriate to pause, say, every tenth, or hundredth, time round the loop. In other words, it is the responsibility of the process to decide when or how often to pause. Idle time computation This example illustrates one of the most common forms of idle active object. It is the form that would be used, say, to reformat a portion of text, following the insertion or deletion of characters. It includes the ability to cancel and restart the operation on receipt of a further event (for example, another keypress) which invalidates a partially complete operation. Note that the ao_run method is subclassed so that no use is made of the functionality of arDLE's ao_run method. CLASS exidle aidle example idle object { REPLACE ao_cancel to reset the processing state REPLACE ao_run perform a unit of processing PROPERTY { WORD counter; records the current processing state } } METHOD VOID exidle_ao_cancel (PR_EXIDLE *self) { self—>exidle.counter=0; p_supersend2 (self,O_AO_CANCEL) ; } 12-3 OLIB REFERENCE METHOD INT exidle_ao_run(PR_EXIDLE *self) { p_printf ("Processing stage %d",self->exidle.counter+t+) ; if (self->exidle.counter<3) p_send2 (self, O_AO_QUEUE) ; return (RUN_ACTIVE USED); } The active object is run as illustrated below, following the action (say, the insertion of a character) which necessitates the processing. GLREF_D PR_EXIDLE *exidle; p_send2 (exidle,O_AO_CANCEL); /* cancel any partially complete processing */ p_send2 (exidle,O_AO_QUEUE); /* restart the processing */ The above code fragment assumes that the active object exists for the lifetime of the application; its creation and destruction would be handled elsewhere. If the active object is to have a transient existence, it would normally be created (with the appropriate error handling) immediately prior to its use. In this case it would be appropriate for the object to send itself a DESTROY message in its ao_run method on completion of the processing. 12-4 CHAPTER 13 TIMER ACTIVE OBJECT CLASSES This chapter describes the TIMER class and two TIMER subclasses, ANIMATOR and BUZSND. The Trmer class, although not formally an abstract class, must be subclassed to provide a specific ao_run method to process the timer expiry. Class diagram fe ee ¢ active / timer / a? ~\. [or Te ¢ animator/ ~ buzsnd / - mye ) Ne ad —~— C fy f- —_—~ Precursors The reader is assumed to understand: e the active class. e the PLIB/EPOC timer driver services. e = for BuzsND, the EPOC sound driver services 13-1 OLIB REFERENCE TIMER ACTIVE gq priority isactive pcb stat destroy ao_init aorinit ao_queue ao_cancel tm_qabsolute ao_abrun The T1rmer class supplies methods to queue both relative and absolute timers. See the Time, Timers and Dates chapter of the PLIB Reference manual for a description of absolute and relative timers and their differences. Class definition Defined in sub-category file timer.cl (generated header file timer.g). CLASS timer active The timer active object { REPLACE ao_init Opens a channel to an asynchronous timer REPLACE ao_queue Queues a relative timer ADD tm_qgabsolute Queues an absolute timer } Property None. TIMER methods AO_INIT Initialise VOID ao_init (VOID) ; Open a channel to a timer using the device name "T1m:" by supersending the ao_tnrT message. This uses the PLIB p_open function. The open timer channel handle will be stored in active.pcb if the timer was successfully opened. Calls p_1eave on error. J AO_QUEUE Queue (relative) VOID ao_queue(UINT lsw, UINT msw); VOID ao_queue(ULONG time) ; (conceptual) Queue a request on the timer for a relative timeout (using active.stat as the completion status word) where time is the required time interval to the timer completion in tenths of a second. The uLonc time is actually passed in the message as two UINT parameters, 1sw (least significant word) and msw (most significant word). Sets active.isactive tO TRUE. Only one timer request (either relative or absolute) may be outstanding at any one time since active.stat is used for either timer request. The timer will receive an ao_RUN message on expiry of the timeout. 13-2 13. TIMER ACTIVE OBJECT CLASSES J TM_QABSOLUTE Queue (absolute) VOID tm_qabsolute(UINT lsw, UINT msw); VOID tm_qabsolute(ULONG time); (conceptual) Queue a request on the timer for an absolute timeout (using active.stat as the completion status word) where time is the absolute system time at which the timer is to complete. The uLonc time is actually passed in the message as two uINT parameters, 1sw (least significant word) and msw (most significant word). Sets active.isactive tO TRUE. Only one timer request (either relative or absolute) may be outstanding at any one time since active.stat is used for either timer request. The timer will receive an ao_RUN message on expiry of the absolute timeout. ANIMATOR gq own priority message isactive interval The anrmator class supplies the functionality to send a message to an object at regular intervals, the message, object and time interval being specified at initialisation. Since the intention is that antmator will be used to drive an animation sequence, it runs at a priority which is much higher than that normally used by timers but which, at the same time, is less than PRIORITY_ACTIVE_WSERV So that window server events - particularly redraw events resulting from the animation - are not blocked. The actual priority is set to PRIORITY_ACTIVE_WSERV - 1. Class definition Defined in sub-category file timer.cl (generated header file timer.g). CLASS animator timer Sends regular messages eg to drive animation { REPLACE ao_init REPLACE ao_run TYPES { typedef struct Must be in property order { PR_ROOT *own; send messages to this object INT message; message number to send INT interval; delay between subsequent messages in tenths of a second INT first; delay before first message back in tenths of a second } IN_ANIMATOR; } PROPERTY { PR_ROOT *own; send messages to this object INT message; the number of the message to send INT interval; interval between messages } 13 -3 OLIB REFERENCE Property animator.own the handle of an object to which messages will be sent (this will normally be the object which owns the instance of anrmaTorR) animator.message the number of the message to be sent to animator.own animator.interval the time interval, in tenths of a second, between the sending of two successive messages ANIMATOR methods AO_INIT Initialise VOID ao_init (IN_ANIMATOR *pin) ; Initialise the animator object by: ¢ supersending an AO_INIT message, which opens a timer channel e setting active.priority to PRIORITY_ACTIVE_WSERV-1 and sending the application manager an AM_ADD_TASK message to add the animator object to the application manager's active object task queue. e setting up animator.own, animator.message and animator.interval from the data pointed to by pin. e sending itself an AO_QUEUE message with the interval timeout value specified by pin->first. In effect, this defines the time interval before the animator object receives the first Ao_RUN message. AO_RUN Run INT ao_run(VOID) Handle the completion of the timer request by: e sending the message animator.message to the object animator.own e sending itself an AO_QUEUE message with the timeout value specified by animator.interval e returning RUN_ACTIVE_USED BUZSND ACTIVE TIMER BUZSND q snd priority sndrep isactive sndnum pcb snddelay stat sndvolume h_done m_done tm_qabsolute ao_init aorinit ao_cancel aerqueu ao_queue ao_run The suzsno class provides the means for an application to generate alarm sound sequences. Each alarm sequence consists of one of two possible sounds repeated eight times with a two second interval between each sound. The volume of the sound is increased with each repetition. 13-4 13. TIMER ACTIVE OBJECT CLASSES The sound itself can be either a 'rings' sequence or a 'chimes' sequence and is selected when an ao_INIT message is received. The sounds are generated by means of the sound driver as described in the Sound chapter of the I/O Devices Reference manual. Since only one user may have access to the sound system at any one time, BUZSND Serialises multiple access requests from different applications. To avoid monopolising the sound driver, the channel is opened and closed for each sound in the sequence. This active object is interesting, in that it may have an outstanding request on either the sound or the timer channel, but not both at the same time; the two channels alternately use active.isactive and active.stat. The descriptions of the ao_cance1 and the ao_run methods include sample code to clarify the explanation of the techniques involved. Class definition Defined in sub-category file timer.cl (generated header file timer.g). CLASS buzsnd timer Buzzer sound generator { REPLACE ao_init Init timer and add to appman task list REPLACE ao_cancel Cancel the timer or sound REPLACE ao_queue Start a sound REPLACE ao_run Handle next step of sound sequence PROPERTY { UBYTE *snd; Open sound channel handle UWORD sndrep; Number of repeats to do UWORD sndnum; Which sound number to use UWORD snddelay; Delay between repeats UWORD sndvolume; For SND: growing volume PR_ROOT *h_done; Handle to receive completion message UWORD m_done; Method number for above } } Property buzsnd.snd the channel handle of the sound driver, while the sound driver is being used. buzsnd.sndrep the remaining number of repetitions in the current sound sequence buzsnd.sndnum which sound to use (0 for a 'rings' sequence or | for a 'chimes' sequence) buzsnd.snddelay the time, in tenths of a second, of the delay between successive sounds buzsnd.sndvolume the volume of the current sound in the sequence buzsnd.h_done NULL, or the handle of the object to which a message is sent on completion of the sound sequence buzsnd.m_done the message number to be sent on completion of the sound sequence BUZSND methods AO_INIT Initialise VOID ao_init (UINT sndnum) ; Open a timer device channel by supersending an ao_InrIT message to the T1mer superclass, store sndnum (either a O for a 'rings' sequence or a | for a 'chimes' sequence ) in buzsnd.sndnum, Set appman.priority to PRIORITY_ACTIVE_REPEATER and send the application manager an aM_ADD_TASK message. Calls p_1eave on error, typically with the error z_GEN_NOMEMoRY. 13-5 OLIB REFERENCE J AO CANCEL Cancel VOID ao_cancel (VOID) Cancel either the timer or the sound driver, whichever is currently running. If the sound channel is open (buzsnd.snd is non-zero), any outstanding request is cancelled using the PLIB I/O p_rcancet service. The sound channel is closed and buzsnd.snd is set to NULL so that further closes are harmless. In all cases the ao_cance1 method then supersends an ao_caNcEL message to cancel any outstanding timer request. As with all ao_cance1 methods, this is harmless if there is no outstanding request. Finally, buzsnd.sndrep and buzsnd. snddelay are Set to their starting values of 8 and 20 (i.e. 20 by 1/10th second) respectively, and buzsnd. sndvolume is set to one of two starting values depending on whether the user has set the machine to generate loud or quiet sounds. METHOD VOID buzsnd_ao_cancel (PR_BUZSND *self) { if (self->buzsnd.snd) /* destroy may call cancel if init fails */ { p_iow2 (self->buzsnd.snd,P_FCANCEL); /* all harmless if not running */ p_waitstat (&self->active.stat); /* see note ++ */ self—>active.isactive=FALSE; /* see note ++ */ p_close(self->buzsnd.sndqd); self-—>buzsnd.snd=NULL; } p_supersend2 (self,O_AO_ CANCEL); /* timer cancel is harmless if not running */ self—>buzsnd.sndrep=8; self->buzsnd.snddelay=20; /* in tenths of a second */ self—>buzsnd.sndvolume=(p_getsnd() &E_SOUND_LOUD) ? (E_SOUND_MIN_VOLUME-1) *2+1: (E_SOUND_MIN_VOLUME) *2+1; } While the calculation for buzsnd.sndvolume in the last line of the code is obscure, it does represent the most efficient way (in conjunction with the ao_run method) of calculating a gradually increasing volume. Note For the byte-conscious programmer, these two lines (marked ++) are not strictly necessary. These actions will be performed within the p_supersend of an ao_canceL which follows a few lines further down. This relies on the fact that the timer and sound channels share the same status word and never have simultaneous outstanding requests. J AO_QUEUE Queue VOID ao_queue(PR_ROOT *handle, UINT method); Start the generation of a sound sequence. Any currently outstanding sound being generated is cancelled by sending itself an ao_caNcEL message, which also resets the sound control parameters buzsnd. sndvolume, buzsnd.sndrep and buzsnd. snddelay to their starting values. The handie and method values are stored in buzsnd.h_done and buzsnd.m_done respectively. The sound generation sequence is started off by setting active.isactive to TRUE and calling p_iosignal. The ao_run method will be called by the active object scheduling code in the application manager when no other events of higher priority are outstanding. AO_ RUN Run INT ao_run (VOID) Make alternate requests for a sound or a timeout until the sound sequence is complete. If the last to run was the sound driver: e the sound driver is closed and buzsnd. snd is set to NULL e if the sound sequence is not complete, the timer is queued by supersending an ao_QUEUE message with a timeout as defined by buzsnd.snddelay (2 seconds). 13 - 6 13. TIMER ACTIVE OBJECT CLASSES if the sound sequence is complete and buzsnd.h_done is non-zero, a buzsnd.m_done message is sent to buzsnd.h_done. If the last to run was the timer: an attempt is made to open the sound driver if the sound driver is currently busy, a five second timeout is queued if the sound driver has been disabled then the sound sequence is deemed to have completed and the completion message is sent, as described above if the sound driver cannot be opened for any other reason (e.g. insufficient memory being available) then p_ieave is called. if the sound driver is opened successfully, the volume is adjusted so that it gradually becomes louder and an asynchronous request is made to generate an alarm sound (this branch requires active.isactive to be explicitly set to TRUE) In all cases the method returns RUN_ACTIVE_USED. METHOD buzsnd_ao_run(PR_BUZSND *self) { INT ret; UWORD delay; UBYTE bb[10]; E_SOUND c; if (!self->buzsnd.snd) { /* last to run was the timer */ bb[0]='S';bb[1]='N';bb[2]='D';bb[3]=':';bb[4]=0; ret=p_open (&self—>buzsnd.snd, &bb[0]); if ((ret==E_FILE_LOCKED) || (ret==E_GEN_INUSE) ) { /* busy - try again later */ delay=50; /* 5 seconds */ p_supersend4 (self,O_AO_QUEUE,delay,0); /* last 2 parameters are a LONG */ } else { if (ret==E_GEN_FAIL) /* sound driver disabled */ goto sendOwnerDone; /* immediate completion (silent alarm) */ f_leave (ret); p_iow3 (self—>buzsnd.snd, P_FSENSE, &c) ; c.volume=(self-—>buzsnd.sndvolume-—) >>1; p_iow3 (self—>buzsnd.snd,P_FSET, &c) ; p_ioc4 (self—>buzsnd.snd, E_FALARM, &self—>active.stat, &self-—>buzsnd.sndnum) ; self—>active.isactive=TRUE; else { /* last to run was the sound */ p_close(self->buzsnd.snd); self—>buzsnd.snd=0; if (--self->buzsnd.sndrep) { p_supersend4 (self,O_AO_QUEUE, self—>buzsnd.snddelay, 0); /* last 2 pars are a LONG */ } else { if (self->buzsnd.h_done) + p_send2 (self-—>buzsnd.h_done, self—>buzsnd.m_done) ; } return (RUN_ACTIVE_USED) ; } 13-7 CHAPTER 14 FILE ACTIVE OBJECTS This chapter describes the ractive class (subclassed by all active objects which perform asynchronous operations on files) and its FScAN, FNODE, Fcasy and Fcsync subclasses. Precursors The reader is assumed to understand: e the acTIvE class e the file server and file system services as described in the Files chapter of the PLIB Reference manual Class diagram i ¢ active / Ss ) 7 fe es ¢ factive / pe aw fo ae ee ae la fscan / ¢ fnode / la feasy / ‘4 fesyne / 2. =i) > a fee FS _) Qo Ke er ee ke ee FACTIVE ACTIVE FACTIVE q owner priority isactive pcb stat destroy fa_close ao_init ao_cancel ao_abrun ao_queue ao_run 14-1 OLIB REFERENCE The ractrve class provides the basic functionality of all active objects that perform operations on files. Although not formally an abstract class, ractrve must be subclassed to be useful. The subclass will, in general, need to supply at least an ao_queue and an ao_run method. Although the file server does not support a cancel service (see the Files chapter of the PLIB Reference manual) ractive supplies an ao_cance1 method which simulates the cancelling of an outstanding asynchronous request. This allows the coding of file-related active objects to follow the style of coding for other active objects which, for example, routinely send an ao_caNcEL message prior to destruction of the instance. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS factive active File active object superclass { REPLACE ao_init Set priority and am_add_task REPLACE ao_cancel Simulate cancel and close file REPLACE ao_abrun Cancel and supersend ADD fa_close Close and set pcb to NULL PROPERTY { UBYTE *owner; Owning object } } Property factive.owner the handle of the owning object, for use by subclasses, for example, to report the completion of file activity - not used by racTIvE FACTIVE methods & AO_INIT Initialise VOID ao_init (UBYTE *owner) ; Initialise the file active object by: e — setting factive.owner to owner, the handle of the owning object e setting active.priority to PRIORITY_ACTIVE_FILES e sending the application manager an AM_ADD_TASK message to add the file active object to the application manager's active object task queue & AO CANCEL Cancel VOID ao_cancel (VOID); Cancel any outstanding file server event and close any open file. This is harmless if there is no outstanding event. Note that the cancellation is simulated since the file server does not support a cancel service (see Asynchronous file operations in the Files chapter of the PLIB Reference manual). The end effect is, however, indistinguishable from a true cancel in that, if a file server event is outstanding, active.stat is set to E_FILE_CANCEL and active.isactive iS set tO FALSE. In addition, this method sends an ra_cLosE message to ensure that any open file is closed. 14-2 14 FILE ACTIVE OBJECTS AO_ABRUN Handle error VOID ao_abrun (VOID) ; Send itself an ao_caNcEL message to cancel any outstanding file activity and close any open file and then supersend an Ao_ABRUN message. This may leave with any error that could arise in the superclass ac_abrun method. & FA_CLOSE Close any open file VOID fa_close(VOID); Close (with p_close) any open file whose handle is in active.pcb, and set active.pcb tO NULL. This method does not call p_ieave. It is a requirement that any subclass must not call p_leave. FSCAN ACTIVE FACTIVE q owner flags priorityt index isactive pname pcb match stat delim finfo cork pcbarr name destroy #a—cetese fs_matchname ao_init fs_fscan ao_cancel ao_queue ao_abrun ao_run fa_close fs_fscan_end fs_dirname fs_filename fs_end_dirlist FSCAN Is an abstract subclass of ractive which provides the basic mechanisms for scanning a filing system by reading the content of one or more directory files. It is designed to be independent of any particular filing system and can be used, for example, with either Macintosh or DOS-compatible filing systems. Subclasses of rscan may be used to scan the filing system to select files which match any of a variety of criteria. The subclass must supply the action(s) required when a matching directory or file is found. It may be noted that on completion of the scan, the whole of each relevant directory file will always have been read; files are eliminated by the matching process within the rscan code. This allows, for example, subclasses of rscan to extract multiple file specifications in one scan of the directory file or to build file name extension lists. 14-3 OLIB REFERENCE Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fscan Scans the directory structure for files/dirs and creates variable str arrays { factive REPLACE ao_queue REPLACE ao_run REPLACE fa_close ADD fs_matchname ADD fs_fscan DEFER fs_fscan_end No more files/dirs DEFER fs_dirname DEFER fs_filename DEFER fs_end_dirlist End of that subdir CONSTANTS { Queue a directory read Process read completion Close all open directory files Match a found name Start a directory scan Next directory name from scan Next file name from scan ! Which types of files caller wants FS_WRITABLE FS_HI DDEN FS_SYSTEM FS_DIRECTORIES FS_MO DIFIED FS_ALL_FILES FS_FI LE_TYPE P_FAWRITE opposite of DOS Read-only attribute P_FAHIDDEN as DOS Hidden attribute P_FASYSTEM as DOS System attribute P_FADIR P_FAMOD as DOS Archive attribute P_FAREAD (FS_HIDDEN|FS_SYSTEM) FS_INCLUDE_SUBDIRECTORIES 0x1000 Want files from subdirectories FS_INCLUDE FS_EN D_DIRLIST FS_PARSE_NAME FS_MAX_DIRLEVE } PROPERTY { UWORD UWORD UBYTE UBYTE UBYTE } } Property fscan. fscan. fscan. fscan. fscan. fscan. 14-4 flags index pname match delim finfo flags; index; *pname; *match; delim[2] P_INFO finfo; P_FPARSE crk; UBYTE *pcbarr[FS_MAX_DIRLEVELS] ; UBYTE name[P_FNAMESIZE]; 0x2000 Called dirname because of include 0x4000 Called end dir list 0x8000 Set if generated name to be parsed LS 32 Max number of sub dir levels Controlling flags Subdir array index Pointer into name[] for read Pointer to file name match , File Info Parsed file info controlling flags, some of which should be set up by the owner before sending an Fs_FSCAN message the index of the first free entry in the fscan.pcbarr array. It should not be accessed by any subclass. a pointer to the file name and extension within the full file specification in the fscan.name buffer. Owners and subclasses should treat this as a read- only field. a pointer to a string used to match file names during a scan. It may be set up by an owner before sending an rs_Fscan message. temporary storage for the directory delimiter character (assumed to be a single character). It should not be accessed by any subclass. the PLIB p_inro data for the current file, that is, the file whose name has last been read from a directory file. This information may be read, but should not be modified, by an owner. 14 FILE ACTIVE OBJECTS fscan.crk provided the rs_parsE_NawE flag is set in fscan. flags, this contains the PLIB p_rparse data for the current file, whose full file specification is in the fscan.name buffer. This information may be read by an owner. fscan.pcbarr an array of up to Fs_MAX_DIRLEVELS handles of open directory files. This should not be accessed by a subclass. fscan.name the full file specification of the current file. An owner may read this name directly or may read only the file name via fscan.pname. FSCAN methods AO_QUEUE Directory read VOID ao_queue (VOID) ; Queue a read on the current directory file, setting active.isactive tO TRUE. The action may be modified by the ao_run method resulting from a previous read: e If the previous read produced the name of a directory file and the scan is to extend into nested subdirectories (fscan. flags includes rs_INCLUDE_SUBDIRECTORIES) the current value of active.pcb Is stored in the fscan.pcbarr array and the new subdirectory is opened before the read request is made. A maximum of 32 levels of subdirectory may be open at any one time. e If the previous read detected that there were no more files in the current directory and directories have been nested, then the most recently nested subdirectory handle is restored into active.pcb from the fscan.pcbarr array before the read request is made. Any error causes p_leave to be called. AO_RUN Process read completion INT ao_run(VOID); Process the completion of the read of a file name from a directory file and return RUN_ACTIVE_USED. If the read completed with an £_rF1LE_zoF error, indicating that there are no further entries in the current directory file:- e the file is closed and active.pcb is Set to NULL. e The delimiter character is picked up (the last character before the filename) and placed in fscan.delim. If directories are nested:- @ an FS_END_DIRLIST message is sent to indicate the end of a directory, but not the end of the scan. e the run method completes and returns RUN_ACTIVE_USED If directories are not nested:- @ an FS_FSCAN_END message is sent to indicate the end of the scan. e the run method completes and returns RUN_ACTIVE_USED If the read completed successfully:- e if the file name is a volume name, the name is discarded and a new name is requested by calling the FScAN ao_queue method directly. e if none of the flags rs_WRITABLE, FS_HIDDEN, FS_SYSTEM, FS_MODIFIED, FS_ALL_FILES Is Set, the name is discarded and a new name is requested by calling the rscan ao_queue method directly. e if the file is a (DOS) . or .. directory file, the name is discarded and a new name is requested by calling the rscan ao_queue method directly. 14-5 OLIB REFERENCE e if the file is a directory file and either of the rs_DIRECTORIES OF FS_INCLUDE_SUBDIRECTORIES flags is set (meaning that there is interest in the directory file name itself or in subdirecrtories) then an Fs_DIRNAME message Is sent. If neither flag rs_DIRECTORIES or FS_INCLUDE_SUBDIRECTORIES Is set, the name is discarded and a new name is requested by calling the rscaNn ao_queue method directly if the file is not a directory file:- e flag is set, then the file name in fscan.name is parsed (using p_fparse) with the parsed file name information written to fscan.crk. e an FS_MATCHNAME message is sent. If this returns FaLsz, indicating that the file name does not match the specified attributes and (wildcard) name, the name is discarded and a new name is requested by calling the rscan ao_queue method directly. If the rs_maTcHNamE message returns TRUE then the read of a valid, matching, file name is indicated by sending an rs_FILENAME message. e the run method completes and returns RUN_ACTIVE_USED. All errors, other than the z_F1LE_zor error discussed above, result in p_leave being called. & FA_CLOSE Close directory files VOID fa_close (VOID) ; Supersend an ra_cLosgE message and close all open directory files. & FS_MATCHNAME Match a found name INT fs_matchname (VOID) ; Check that the file matches the specified combination of rs_MoDIFIED, FS_HIDDEN and Fs_systTen flags. If these tests succeed the file name (pointed to by fscan.pname) 1s tested for a match with the (wildcard) name pointed to by fscan.match. Note that the name match is case-sensitive. Since rscan is designed to work independently of any particular filing system, matching uses a true wildcard string match (as opposed to a DOS-specific file system wildcard match) to select files. Thus the wildcard string "*" will select all files (including those with an extension, unlike DOS). This also means that more than one part of a file name can be wildcarded with the '*' character, to find, for example, files matching "*fred.*". Returns true if there is a match, otherwise raLsE. This method is called from within the ao_run method. A subclass may replace this method to provide an alternative name matching algorithm. FS FSCAN Start a directory scan VOID fs_fscan(UBYTE *path); Start the scan of the file system for directories and files. The file specification pointed to by path is parsed to extract the directory in which the scan is to start. Provided this initial path name does not need to be preserved, path may point to a file specification in fscan.name (this field is overwritten during the scan). If the specified directory is opened successfully, an ao_quEUE message is sent to start the scan. Any error causes p_leave to be called. It is assumed that fscan.match and fscan. flags have previously been set up either by a subclass or by the owner as is done, for example, by many of the methods (such as fman_rename) of the rman class, described in the File Management Classes chapter. The fscan.match field should be set to point to a suitable wildcard string. 14-6 14 FILE ACTIVE OBJECTS The fscan. flags field should contain a bitwise combination of one or more of the following values: FS_ALL_FILES include all non-directory files that are neither hidden nor system files FS_WRITABLE aS FS_ALL_FILES, but excludes write-protected files (see below) FS_HIDDEN include hidden files FS_SYSTEM include system files FS_DIRECTORIES include directory files FS_MODIFIED exclude unmodified files (include only files that have p_ramop set) FS_INCLUDE_SUBDIRECTORIES extend the scan to subdirectories FS_PARSE_NAME parse file names before reporting them The supplied £s_matchname method does not test the rs_wR1ITABLE flag which therefore has the same effect as the rs_att_Fiues flag. A subclass fs_matchname method may implement this flag as follows: if ((self->fscan.flags&FS_WRITABLE) && !(self->fscan.finfo.status&P_FAWRITE) ) return (FALSE) ; return (p_supersend2 (self,O_FS_MATCHNAME) ) ; Deferred FSCAN methods FS FSCAN_END Scan completion VOID fs_fscan_end(VOID); A deferred method indicating the end of the scan and that there are no more files or subdirectories to scan into or report back. This message is sent from within the ao_run method. By the time it is sent, all levels of directory files will have been closed. FS DIRNAME Next directory name VOID fs_dirname (VOID) ; A deferred method indicating that a directory file matching the initial specification has been found. This message is sent from within the ao_run method, provided that fscan. flags includes either FS_DIRNAME Of FS_INCLUDE_SUBDIRECTORIES. On receipt of this message fscan.finfo contains the file information for the directory file, fscan.name contains its full file specification and the fscan.pname points to the directory file name within the full file specification. These fields should be regarded as read only. In order to obtain further files or directories, this method must restart the scan by sending an ao_QUEUE message. FS_FILENAME Next file name VOID fs_filename (VOID) ; A deferred method indicating that a file name matching the initial specification has been found. This message is sent from within the ao_run method. On receipt of this message fscan.finfo contains the file information for the file, fscan.name contains its full file specification and the fscan.pname points to the file name within the full file specification. If fscan.flags includes rs_PARSE_NaME, then fscan.crk contains the parsed file name information. These fields should be regarded as read only. In order to obtain further files or directories, this method must restart the scan by sending an ao_QUEUE message. 14-7 OLIB REFERENCE FS _END_DIRLIST End of subdirectory VOID fs_end_dirlist (VOID); A deferred method indicating that the end of a subdirectory has been reached. This message is sent from within the ao_run method provided that fscan. flags includes rs_INCLUDE_SUBDIRECTORIES. It is only reported at the end of a nested subdirectory and not when the end of the directory in which the scan started is reached. The directory file will be closed before this message is sent. On receipt of this message fscan.name contains the full file specification of the directory file and the fscan.pname points to the file name within the full file specification. The information in fscan.finfo and fscan.crk is not valid. In order to obtain further files or directories the scan must be restarted by sending an aco_quEuUE message at some point. FNODE ACTIVE FACTIVE q owner flags priority pname isactive oldname pcb pcb stat destroy #a—cteose fn_list ao_init ao_queue ao_cancel ao_run ao_abrun fa_close fn_end_list fn_nodename In EPOC there are multiple filing systems, three of which are ROM::, LOC:: and REM::. The rnopE abstract class provides the basic mechanisms to generate filing system node and device lists. The rnope methods read an item in the filing system (node) list and, if that node supports multiple devices (drives) read the device list to generate a node: :device.:\ name. Filing systems (such as ROM-::) that do not support multiple devices are ignored. It is possible to restrict the list to the LOC:: filing system. Since filing systems are dynamic under EPOC, the names generated may vary between invocations. 14-8 14 FILE ACTIVE OBJECTS Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fnode factive Node/Device name list generator { REPLACE ao_queue Read from appropriate channel REPLACE ao_run Process read completion REPLACE fa_close Closes both open channels ADD fn_list Start to generate a list DEFER fn_end_list Handle list generate completion DEFER fn_nodename Process an item in the list CONSTANTS { FNODE_NODE_ARRAY 0x01 Reading node list FNODE_DEVICE_ARRAY 0x02 Reading device list FNODE_LOCAL_ONLY 0x04 Read local node list only } PROPERTY { UWORD flags; Controlling flags UBYTE *pname; Where to read into UBYTE *oldname; Node read ptr UBYTE *pcb; Node pcb while reading device list } } Property fnode.flags internal controlling flags fnode.pname the offset into the data buffer of where to write the node or device name. It should not be accessed by an owning object. fnode.oldname the offset into the data buffer of where to read the next node name. It should not be accessed by an owning object. fnode.pcb while reading a device list, the handle of the opened node list file is saved here. It should not be accessed by an owning object. FNODE methods & AO_QUEUE Read from appropriate channel VOID ao_queue (VOID) ; Queue a read on either the node list or device list, depending on which is appropriate at the time and set active.isactive tO TRUE. Note that node information as would be written to a p_NInFo structure is not requested in the read. AO_RUN Process read completion INT ao_run(VOID); Process the completion of a read that was initiated by the ao_queue method and return RUN_ACTIVE_USED. The read may have been from either the node list or a device list. The two cases are discussed under their respective headings. Node List Read The completion status is tested for an E_FILE_£oF error, indicating that no further nodes exist; if this is the case, the scan is terminated by closing the node list and sending an rN_END_LIsT message. Otherwise, the node is checked for validity:- e the node must support multiple devices e if the FNopE_LocaL_onty flag is present, only the LOC:: node is valid. 14-9 OLIB REFERENCE For a valid node, the device list for that node is opened and an ao_QuzuUE message sent to read an item from the device list, otherwise an ao_QuEUE message is sent to read a further item from the node list. Device List Read The completion status is tested for an =_FILE_EoF error, indicating that no further devices exist on the current node. If the completion status is E_FILE_EOF:- e the device list is closed @ an AO_QUEUE message is sent to read the next item from the node list. If the read completed successfully, an rN_NODENAME message is sent. The owner is responsible for re-queuing a read on the node/device list by sending an ao_quEUE message at some future point after sending the rn_NODENAME message. For all errors other than the z_riLe_zor errors discussed above, p_leave is called. & FA_CLOSE Close both open channels VOID fa_close (VOID) ; Close any open node and device lists setting both the open handles to nun. The node list is closed by supersending an ra_cLosE message; the device list is closed by calling p_close. FN_LIST Start list generation VOID fn_list (UBYTE *pname, UINT flags); Start the scan to generate node: :device:\ names by opening the node list and sending an ao_QuEUE message. The user-supplied buffer at pname is assumed to be at least p_rNames1zeE bytes in length. It is used as the output buffer for each generated name and hence, must be preserved until an FN_END_LIST message is received. The user may access this buffer only when no read is outstanding, for example, during the processing of the deferred rN_NODENAME message. The value of f1ags may be either rNopE_LocAL_oNLy to read only the LOC:: filing system, or nuu1 to read all filing system device lists. Deferred FNODE methods FN_END LIST Handle completed list VOID fn_end_list (VOID) ; A deferred method indicating that the scan is complete and that all node: :device:\ names have been generated. On receipt of this message the node and device lists will have been closed. The user-supplied buffer (see the fn_1ist method) may now be discarded. FN _NODENAME Process a list item VOID fn_nodename (VOID) ; A deferred method indicating that a node: :device:\ name has been generated. The name may be read from the user-supplied buffer (see the fn_1ist method). The user is responsible for restarting the scan for the next name by sending an ao_QuEUE message. 14-10 14 FILE ACTIVE OBJECTS FCASY ACTIVE FACTIVE q owner recvname priority flags isactive pcb stat destroy ao_cancel ao_init ao_run aereancet ao_queue ao_abrun fa_close fc_write fc_open fc_request_comp The rcasy abstract class subclasses ractIve to provide a set of methods to perform a segmented read or write of a single file. In other words, it allows a file to be read or written to in a finite number of discrete portions. This permits a large file to be processed (which would otherwise be impossible due to memory constraints) Fcasy does not support a combination of reading and writing to the same file. Typical uses are in copying a file or in XMODEM file transfer, where the source and target files are each represented by a separate instance of a subclass of rcasy. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fcasy factive File copy/save/load asynchronous routines { REPLACE ao_cancel Abandon and clean up correctly REPLACE ao_run Maybe cleanup and send itself fc_request_comp REPLACE ao_queue Async file read REPLACE fa_close Close file, set recname to NULL ADD fc_write Async file write ADD fc_open Open/create file for load/save DEFER fc_request_comp That async request has completed CONSTANTS { ! Display info flags FCOPY_DISP_SFTYPE 0x01 FCOPY_DISP_SFSIZE 0x02 FCOPY_DISP_SFDATE 0x04 FCOPY_DISP_SFNAME 0x08 FCOPY_DISP_RFTYPE 0x10 FCOPY_DISP_RFSIZE 0x20 FCOPY_DISP_RFDATE 0x40 FCOPY_DISP_RFNAME 0x80 FCOPY_DISP_BLKSIZ 0x100 FCOPY_DISP_BLKNO 0x200 FCOPY_DISP_PROTOCOL 0x400 ! Controlling Flags FCASY_READ_QUEUED 0x01 FCASY_WRITE_QUEUED 0x02 } 14-11 OLIB REFERENCE TYPES { typedef struct { UWORD flags; FCOPY_DISP_... flags indicate which fields are valid UWORD protocol; Which protocol being used UWORD blksiz; Size of each block UWORD blkno; Current block number UWORD sftype; Source file type UWORD rftype; Receive file type ULONG sfsize; Source file size ULONG rfsize; Receive file size ULONG sfdate; Source file last modification date ULONG rfdate; Receive file last modification date UBYTE *sfname; Source file name (source for reads) UBYTE *rfname; Receive file name (destination for writes) } FCOPY_DISP; } PROPERTY { UBYTE *recvname; Receive file name UWORD flags; Controlling flags } } Property fcasy.recvname a pointer to the name of the file being written to (nut if the file is being read) fcasy.flags internal controlling flags; while a request is outstanding, contains a value indicating the nature of the request (either rcasy_READ_QUEUED or FCASY_WRITE_QUEUED) FCASY methods & AO_CANCEL Cancel request VOID ao_cancel (VOID); Cancel any outstanding read or write by supersending an ao_cANCEL message. If the instance represents a file that is opened for writing, the file is then explicitly deleted. & AO_QUEUE Read request VOID ao_queue(UBYTE *buf, UWORD *plen); Queue a read of *pien bytes into buf from the file whose channel handle is in active.pcb. Sets active.isactive to TRUE and sets the rcAsy_READ_QUEUED flag in fcasy. flags to indicate a read request. Since the read is asynchronous, the memory pointed to by buf and plen must be preserved until the request completes. & FC_WRITE Write request VOID fc_write(UBYTE *buf, UWORD *plen); Queue a write of *pien bytes from bug to the file whose channel handle is in active.pcb. Sets active.isactive to TRUE and sets the FcAsy_WRITE_QUEUED flag in fcasy. flags to indicate a write request. Since the write is asynchronous, the memory pointed to by buf and pien must be preserved until the request completes. 14-12 14 FILE ACTIVE OBJECTS AO_RUN Process read or write completion INT ao_run(VOID); Process the completion of either a read or a write request. Note that the object is expected to handle the completion of either a read request (Aao_QUEUE) or a write request (FC_WRITE) but not a mixture of the two. Clears the FcASY_READ_QUEUED and FCASY_WRITE_QUEUED bits in fcasy. flags. If active.stat 1S TRUE (indicating an error), it sends itself an ao_cancEeL message. This closes the file on detection of a read or write error (read errors include z_F1LE_EOoF); in the case of a write it ensures that the partially written file is deleted. Sends itself an rc_REQUEST_comp message before returning RUN_ACTIVE_USED. & FA_CLOSE Close the file VOID fa_close (VOID) ; Close the file by supersending an ra_cLosE message. Sets fcasy.recvname (which is used on detection of an error to delete any partially written file) to nuLL. & FC_OPEN Open a file INT fc_open(TEXT *name, INT mode, FCOPY_DISP *pdinfo) ; Open the file with name in the buffer pointed to by name (which must be at least p_rwames1ze bytes long) in the specified mode and fill in the struct at pdinfo with as much information as possible about the file. The items that have been written to *pdinfo are indicated by the corresponding flag bits being set in pdinfo->flags. It is assumed that the file is to be opened for either reading or writing, but not both. The value of mode must include one of p_roPEN, P_FCREATE Of P_FREPLACE (otherwise the method returns &_GEN_aRG). Sets pdinfo->sfname tO name and then parses name (using p_fparse) to ensure that the file name is valid. If the file is opened for writing and the specified directory does not exist, it is created automatically. If the file is to be opened for reading (mode includes p_ropen), the file size and last modification date are written to pdinfo->sfsize and pdinfo->sfdate respectively. Depending on whether the file type is binary or text, pdinfo->sftype 1s set to P_FSTREAM Or P_FTEXT. The value of mode is augmented by oring in p_rsuare. If the file type is text, mode is converted to use P_FSTREAM_TEXT (rather than p_FTExT) to optimise the reading of the file. If the file is to be opened for writing the method will return an error if mode includes p_rcreatE and the file exists or if mode includes p_FREPLACE and name specifies a directory file. Otherwise pdinfo->rfname iS set to name and the value of mode is augmented by oring in p_ruppate. If the file is opened successfully fcasy.recvname 1S set to name. The data space pointed to by name must therefore be preserved until the file has been closed. Returns zero if successful, otherwise a negative error number. 14 - 13 OLIB REFERENCE Deferred FCASY methods FC_REQUEST COMP Inform of completion VOID fc_request_comp (VOID); A deferred method, called from within the ao_run method, indicating that the read or write request has completed. If there was a read or write error, the file will already have been closed and, if appropriate, deleted by means of an ao_cANcEL message. The subclass is, however, responsible for checking the completion status (active.stat) and performing any additional error handling. In the absence of such errors, the user is responsible for continuing the operation by sending the next AO_QUEUE Of FC_WRITE message. Errors in this method should result in p_leave being called. FCSYNC ACTIVE FACTIVE FCASY FCSYNC q recvname priority flags isactive pcb stat destroy ao_cancel ao_queue ao_init ao_run fc_write 2 Soe ao_abrun fa_close fe-write fc_open fc_request_comp The rcsync abstract class subclasses rcasy. It converts rcasy to use synchronous file read and write services, otherwise it is identical to Fcasy. As with Fcasy, it is assumed that an instance of a subclass of rcsync is used to either read from or write to a file. Since file access is synchronous, it should only be used in situations where the read and write operations are guaranteed to complete quickly. Thus, it is effectively restricted to use with files on the local filing system. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fcesync fcasy Synchronous file I/O for local file system { REPLACE ao_queue Synchronous file read REPLACE fc_write Synchronous file write } Property None. 14-14 14 FILE ACTIVE OBJECTS FCSYNC methods & AO_QUEUE Read request INT ao_queue(UBYTE *buf, UWORD *plen); Perform a synchronous read of *pien bytes into but from the file whose channel handle is in active.pcb by supersending the ao_quEvE message and then waiting (with p_waitstat) ON active.stat for the read to complete. Sends itself an ao_cancet (which closes the file and sets active.stat to E_FILE_CANCEL) if the read completes with an error. In particular, since =_FILE_EoF is not distinguished from other errors, the file will be closed automatically on reading to the end of the file. On exit active.isactive and fcasy.flags are both guaranteed to be zero. Returns the value of active.stat. & FC_WRITE Write request INT fc_write(UBYTE *buf, UINT len); Perform a synchronous write of 1en bytes from bur to the file whose channel handle is in active.pcb by supersending the rc_wRITE message and then waiting (with p_waitstat) ON active.stat for the write to complete. Sends itself an ao_cance. (which closes and deletes the file, and sets active.stat to E_FILE_CANCEL) if the write completes with an error. On exit active.isactive and fcasy.flags are both guaranteed to be zero. Returns the value of active.stat. 14-15 CHAPTER 15 FILE Lists The classes described in this chapter are concerned with the navigation of the directories of a filing system and with the generation and storage of directory and file name lists. Precursors The reader is assumed to understand: e active objects and the rnopE and Fscawn abstract classes e the vastR and vaxvar variable array classes e the file server node, device, directory and file services e the p_enter and p_leave error handling services Class diagram ra ros sitet de fi ae ti ws ¢ Varoot / ¢ active / ly ) mS x f e f _ Pore ee ‘ ¢ Vafix / ¢ factive / ~“ ) a _ ) min — are a ‘Se f Rey oe? Peay ¢ Vaflat / , vastr / ¢ sh j Va “ {node ? es ue N i pres a vaxvar oe a ee Pale, y pselvar / ¢ Pnode / ee ee ae San 15-1 OLIB REFERENCE PSELVAR nrec ke rlen gran nspc destroy arepta i atest va_count va_compress | va_copy va_delete a—detetem va_reclen va_sort Farinsertm va_replace va_key va_capacity | va_init va_findisgq Lad va_prec va_deletem va_insertisq oe ES va_insertm va_append b va_pbuf va_insert va_search va_compare va_reset Wartest The psetvar class subclasses the vaxvar variable array class. It is intended to be used to store an ordered array of file and directory names and the additional method is tailored to the special sorting schemes needed. This class is defined specifically for use by the pset class, described later. The va_test method assumes that varoot .key.desc specifies ascending or descending order as normal for the variable array classes, but that varoot.key.fold contains one of the psEL_ORDER_xxx values defined below. The varoot .key.ofs and varoot .key.1len fields are not used. The psEL ps_order method writes directly to the varoot .key.desc and varoot .key. fold fields of its component instance of psELvar (as an alternative to providing psELvaR with a subclassed va_key method). Each psE.var record is assumed to be an Rc_vaxvar struct, whose buf field points to an allocated heap cell containing a psEL_REc struct. The contents of this struct are determined by the owning ese. The name field contains either a file name or a directory name. Note that the namien field contains meaningful data only if the names are ordered by extension. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS pselvar vaxvar Holds the files/dir names in order - dir names first then file names { REPLACE va_test CONSTANTS { PSEL_FLAG_TAG Oxl PSEL_ORDER_NAME 0 Order by name alphabetically (default) PSEL_ORDER_TIME al Order by time of creation PSEL_ORDER_DATE 2 Order by date of creation PSEL_ORDER_SIZE 3 Order by size of file PSEL_ORDER_EXT 4 Order by extension } TYPES { typedef struct { UWORD flags; Tagging flags info UWORD namlen; Offset in name of extension P_INFO info; File info UBYTE name[P_FNAMESIZE]; Max file name size buffer } PSEL_REC; } Property None. 15 -2 15 FILE LISTS PSELVAR methods & VA_TEST Compare two records by pointer INT va_test (RC_VAXVAR *precl, RC_VAXVAR *prec2); Compare the two records pointed to by preci and prec2, returning 0 if the two records are equal, <0 if *precl is before *prec2, or >0 if *preci is after *prec2 Each record is assumed to be an Rc_vaxvar struct whose buf field points to a PSEL_REC struct containing either a file name or a directory name. A directory name is always ordered before a file name and directory names are always ordered alphabetically. File names are ordered by one of name, creation time, creation date, size or extension, depending on the PSEL_ORDER_XXx value stored in varoot .key. fold. The result of the comparison is reversed if varoot .key.desc is non-zero. PNODE ACTIVE FACTIVE FNODE q owner flags priority pname isactive oldname pcb pcb stat destroy fea-etese fnulist ao_abrun ao_init ao_queue fn_end_list ao_cancel | ao_run fn_nodename aerab run fa_close The pnove class subclasses the FNopE node and device list generator. It is defined specifically for use by the psEt class, described later. The code contains assumptions that the owner (whose handle is in factive.owner) is an instance of a subclass of pszEL and makes direct calls to PSEL code. The prope methods are designed only to be called via the mechanisms provided within the methods of the PSEL Class, described later in this chapter. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS pnode fnode Used by psel to generate the node/directory array { REPLACE ao_abrun REPLACE fn_end_list REPLACE fn_nodename } Property None. 15 -3 OLIB REFERENCE PNODE methods AO_ABRUN Handle error VOID ao_abrun (VOID) ; Supersend the ao_aprun message and then make a direct call to reset property and component objects of the owner (assumed psEt). FN_END LIST Handle completed list VOID fn_end_list (VOID) ; Process the completion of the node: :device:\ name list. Reads and modifies the property of the owning (PsEL) object to indicate that the list has been changed, and starts the generation of the file list corresponding to the selected, or the default, item in the node: :device:\ list. FN_NODENAME Process a list item VOID fn_nodename (VOID) ; Process a generated node: :device:\ name. Makes a direct call to add the generated name to the owner's (psEL) directory/node name list and then sends itself an Ao_QUEUE message to continue the scan. PSEL ACTIVE FACTIVE FSCAN q owner pfile priority pdir isactive pnode pcb dirent stat flags ascent dirnum setpath builderr fck fspec fs_matchname ao_init fs_fscan ao_abrun ao_queue ao_cancel ao_run fs_filename fa_close fs_dirname fs_fscan_end ps_get_file ps_ascend_path ps_descend_path fs_end_dirlist ps_set_path ps_sense_filename ps_select_direntry ps_drives ps_settag ps_gettag ps_order ps_new_list 15-4 15 FILE LISTS PSEL is an abstract subclass of rscan, providing methods to navigate a filing system and to generate both a node list and a file name list from a wildcard file specification. In addition it provides methods to tag items in its file name list and to retrieve such tagged items. The node list contains device names or directory names at some specified level. The file name list contains files and subdirectories within one particular item in the node list. Although psx uses a number of active object components, it appears to a user as a single active object. A queued request starts the building of one or more of its lists. On completion it reports: e which lists have been changed e whether the file specification has changed e whether the file name list was either built successfully or was left empty because the physical device does not exist (no disk in drive). A subclass of psex is used, for example, to generate and navigate the file lists displayed in the file commands of the Series 3 System application, and in the MC File Manager application. Subclasses of psEL are not expected to replace any of the supplied methods. Note that there is no automatic detection of filing system changes. This means that a file list will be out of date if, for example, another process has created a file in the directory being displayed after the file list was last built. A regeneration of the file name list may be forced by sending a ps_sET_PATH message. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS psel fscan Builds a node/directory array and a file array from a wild card path { REPLACE ao_init REPLACE ao_abrun REPLACE ao_cancel REPLACE fs_filename REPLACE fs_dirname REPLACE fs_fscan_end DD ps_get_file ps_ascend_path ps_descend_path ps_set_path ps_sense_filename ps_select_direntry ps_drives ps_settag ps_gettag DD ps_order DEFER ps_new_list D D D D D D D D pPppprprprpr pe CONSTANTS { PSEL_RESET_DIR_ARRAY 0x01 True if NOT reset node/dir array PSEL_RESET_FILE_ ARRAY 0x02 True if NOT reset files array PSEL_RESET_FSPEC 0x04 True if NOT reset fspec[] PSEL_DEVICE_ARRAY 0x10 Building device list PSEL_DIR_ARRAY 0x20 Building dir list PSEL_FILE_ ARRAY 0x40 Building file list PSEL_DESCEND 0x80 Building due to a descend PSEL_ASCEND 0x100 Building due to an ascend PSEL_SETPATH 0x200 Building due to a set path PSEL_INIT 0x400 Building due to an init PSEL_SELDIR 0x800 Building due to select dir PSEL_ANOTHER_CMD 0x1000 PSEL_GENERATE_DEF 0x2000 PSEL_QUEUED_CMD 0x4000 Running a queued cmd PSEL_CURRENTLY_BUSY (0x100|0x80|0x400|0x200|0x800) PSEL_SET_TAG = PSEL_CLEAR_TAG 0 PSEL_TOGGLE_TAG 1 } 15-5 OLIB REFERENCE PROPERTY 3 { PR_PSELVAR *pfile; File name lists PR_VASTR *pdir; Directory/Node name list PR_PNODE *pnode; Node List Generator UWORD Which directory in list should be highlighted UWORD UWORD UWORD File selector control flags UBYTE *setpath; WORD builderr; P_FPARSE fck; Files list build error to report Currently cracked wildcard file spec TEXT fspec[P_FNAMESIZE]; Current wildcarded file spec } } Property psel. psel. psel. psel. psel. psel. psel. psel. psel. psel. psel. 15 - 6 pfile pdir pnode dirent flags ascent dirnum setpath builderr fck fspec the handle of an instance of pszLvar, which holds the file name list in an array of psEL_ReEc records. A subclass may access this handle, and will typically pass it to display code. (Note that pszLvar's va_pBur method returns a pointer to a PSEL_RECc struct, rather than to the text of a file name.) the handle of an instance of vastr, which holds the array of names for a node or directory list. A subclass may access this handle and will typically pass it to display code. the handle of an instance of pNopz, which generates a node: :device:\ name list. It should not be accessed by a subclass. the index of the current node list item in the psel.pdir VASTR array. It defines the subdirectory for which the file list is built (a value of -1 indicates that no entry is current). A subclass may read this field to indicate which displayed node list entry to highlight. controlling flags used to drive the psx object. A subclass may read the values of the pSEL_RESET_DIR_ARRAY, PSEL_RESET_FILE_ARRAY and PSEL_RESET_FSPEC bit-fields. the outstanding number of directory ascends to be performed (a user may request multiple ascends faster than they can be serviced). It should not be accessed by a subclass. the index of the most recently selected entry from the psel.pdir VASTR array. Typically the user interface will allow the selection of a new entry from this array while eset is busy building the file names array for a previously selected entry. It should not be accessed by a subclass. a pointer to the most recently selected wildcarded file name specification from which the node and file name arrays are to be built. The data space to which it points must be preserved until the arrays have been fully built. It should not be accessed by a subclass. an error number corresponding to a drastic error (such as out of memory, or the removal of a filing system) which occurred while building the node and file name arrays. If no error has occurred the value will be zero. A subclass is expected to test this value to determine the build completion result when the deferred ps_new_List method is called. the parsed file name information for the wildcarded name (contained in psel.fspec) that is currently being used to build the node and file name arrays. A subclass should treat this as a read-only field. the wildcarded file name specification that drives the generation of the node and file name arrays. Typically this field could be used to display the full file name specification corresponding to the arrays that have just been built. A subclass should treat this as a read-only field. 15 FILE LISTS PSEL methods A user is expected to send only those explicit messages that appear in the following list: PS_ASCEND_PATH ascend one subdirectory level PS_DESCEND_PATH descend to a subdirectory PS_SET_PATH set a new path PS_SENSE_FILENAME sense a file list item PS_SELECT_DIRENTRY select a node list entry PS_DRIVES ascend to the drives level PS_SETTAG set/clear a file tag PS_GETTAG get a tagged file PS_ORDER set the file list order The remaining methods are intended for internal use. Many of the above methods cause either or both of the directory/node and file name lists to be rebuilt. If this fails (say, because the pack has been removed or the filing system no longer exists) then the lists are rebuilt at the node: :device:\ level. All such methods result in a (deferred) ps_NEw_LIsT message being sent. Depending on whether rebuilding is required, this message may be sent either before the method returns or at some later time, when building is complete. AO_INIT Initialise VOID ao_init (TEXT *pname) ; Supersends an ao_1n1T message to add itself to the appman queue and then creates and initialises the psel.pdir, psel.pfile (with a granularity of 32) and psel.pnode components. Then starts building the node and file name arrays, using the text pointed to by pname as the initial file name specification. If the initial file specification is not valid (say, because the filing system no longer exists) the default lists are built. These consist of: e anode list containing all currently available node: :device:\ names e a file name list for the directory indicated by the first item in the node list, containing all files that match the initial file name specification. Calls p_leave on error (out of memory). AO_ABRUN Handle error VOID ao_abrun (VOID) ; Handle an error arising during the processing of an ao_RUN message. Discards the contents of the node and file name lists (at least one of which is likely to be only partially built) and sets psel. fspec to contain a null string. Sends a ps_NEW_LIST message, which will normally cause the user interface to display empty lists and then supersends the ao_aBRuN message. This method will only be invoked by out of memory errors or by the disappearance of filing systems during the generation of the lists. & AO CANCEL Cancel list building VOID ao_cancel (VOID); Sends an ao_cAaNCEL tO psel.pnode to cancel any request on the node list generator and then supersends an AO_CANCEL to cancel any file scan request. These two messages ensure that any open channels are closed. Following this, the contents of the node and file name lists are discarded. This method is intended only to be executed as a consequence of psEL receiving a DESTROY message. A user should not, for example, send an ao_caNcEL message when requesting an operation (such as an ascend) before a previous operation has completed. Such a sequence is handled by pset's internal logic and the user should simply request the new operation. 15-7 OLIB REFERENCE FS_FILENAME Add a file name VOID fs_filename (VOID) ; Add a file name to the file name list. Generates a psEL_rec for the file name pointed to by fscan.pname and appends it to the file name list (the list will be ordered when it is complete). Note that, to minimise memory use, the psEL_REc struct is adjusted to the exact length required for its content before it is appended. You should also be aware that the contents of the namien field of the PSEL_REC Struct will only be valid if the file name list is sorted by extension. If the name is added successfully, sends an ao_quEUE message to continue the scan. May call p_leave (E_GEN_NOMEMORY) . FS DIRNAME Add a directory name VOID fs_dirname (VOID) ; Add a directory name to either the node list or the file name list depending on which list is currently being built. If the name is being added to the node list, the full name (taken from the start of the fscan.name buffer) is inserted in alphabetical order. If it is being added to the file name list, only the ‘file name' part (pointed to by fscan.pname) is appended to the list exactly as described for the f£s_filename method. If the name is added successfully, sends an ao_quzuE message to continue the scan. May call p_leave (E_GEN_NOMEMORY) . FS FSCAN_END Process end of a scan VOID fs_fscan_end(VOID) ; Process the end of the building of either the node list or the file name list. If the node list has just been built, the building of the appropriate file name list is started. If the node list has not changed (say, because the build was initiated by a directory ascend request when already at the drives level), the processing is as for the completion of the building of the file name list, described below. If the file name list has just been built, a check is made to see if any additional requests (such as one or more directory ascends) have been made while the list was being built. If so, the appropriate list building is restarted. Otherwise, provided the file name list has changed, it is sorted in the currently specified order and a ps_NEW_LIST message is sent to indicate that list building is complete. PS GET _ FILE Build file name list INT ps_get_file (VOID); Discard the current file name list and send an rs_rscan message to start a scan to build a new list for the directory specified by the current item in the node list. The list will include all subdirectories, together with all file names that match the wildcard file name and extension string contained within the full file specification in the psel. fspec buffer. Returns zero, indicating a successful start of the scan. Calls p_1eave on error. 15-8 15 FILE LISTS & PS ASCEND PATH Ascend one subdirectory level VOID ps_ascend_path (VOID); Start the rebuilding of the node and file name lists for a directory level one higher than that specified by the wildcarded full file specification in pse1.fspec. If the current directory level is at the node: :device:\ level already (if, for example, psel.fspec contains "LOC::A:\*.1MG") then no further ascends can be made. The node list will, however, be re-built because a filing system may have been added or removed since the last time the lists were built. This is the only way in which such a change in the filing system can be recorded in the node list. In such a case the files list will not be re-built unless the current node has disappeared. If this message is received while the lists are in process of being built then the ascend request will be stored internally. On completion of the current build, building will be restarted (without sending a PS_NEW_LIST message) at the new directory level. & PS DESCEND PATH Descend to a subdirectory VOID ps_descend_path(UINT entryno) ; Descend into the subdirectory specified by record number entryno in the file name list and start the rebuilding of the node and file name lists. Does nothing if the lists are currently being built (since the array from which the entry was selected no longer exists). An entryno of -1 (meaning that no list item was selected) has the same effect as a value of 0, specifying the first item. The file name list is checked to ensure that it contains the entry number specified and that the corresponding record is the name of a directory. If either of these tests fail the method does nothing. Otherwise the node and file name lists are rebuilt for the new directory level. & PS SET PATH Set a new path VOID ps_set_path(TEXT *pname) ; Start the rebuilding of the node and file name lists to correspond with the wildcard full file specification pointed to by pname. If the lists are currently being built, the new path name pointer is stored until the appropriate time that the current build can be abandoned and a new build started. In all cases the data space pointed to by pname must be preserved until a ps_NEW_LIST message is received to indicate that the lists have been rebuilt. & PS SENSE FILENAME Sense a file list item INT ps_sense_filename (INT entryno, PSEL_REC **pprec) ; Write, to *pprec, a pointer to the pszEL_ReEc data for the file list item with record number entryno. Returns zero if successful. Does not write to *pprec and returns E_FILE_LOCKED if the list is currently being built. & PS SELECT DIRENTRY Select a node list entry VOID ps_select_direntry (INT entryno) ; Start the building of the file name list for the directory specified by item number ent ryno in the node list. If a file name list is currently being built as a result of an earlier ps_sELECT_DIRENTRY message, the current activity is aborted and the build is restarted for the new list. This provides a rapid response to repeated ps_SELECT_DIRENTRY messages received from the user interface (generated, for example, as the user moves a highlight up and down a displayed list). If a list is being built for any other reason, the PS_SELECT_DIRENTRY message has no effect. 15-9 OLIB REFERENCE © PS DRIVES Ascend to the drives level VOID ps_drives (VOID) ; Start the rebuilding of the node and file name lists to generate a node list containing node: :device:\ names and a file name list containing those items which match the file name and extension contained in the wildcarded full file specification in the psel. fspec buffer. Does nothing if the lists are currently being built. If the lists are currently at the drives level then they are not rebuilt. In either case a PS_NEW_LIST message will be sent to indicate that the list building is complete. & PS _SETTAG Set/clear a file tag INT ps_settag(INT entryno, INT flag); Set, clear or toggle the tag status (held in the flags field of the pszL_rxEc struct) of the file name list item specified by entryno. The tag status will be set if f1ag is ps—EL_sET_TAG, Cleared if f1ag is PSEL_CLEAR_TAG and toggled if flag iS PSEL_TOGGLE_TAG. Returns True if the tag status has been set, and rause if it has been cleared. & PS _GETTAG Get a tagged file INT ps_gettag(INT index, PSEL_REC **pprec) ; Write, to *pprec, a pointer to the psEL_REc of an item in the file name list that has its tag status set, and return either a (positive) value to be used as the index for a subsequent ps_GETTAG message, or E_FILE_EOF if there are no further tagged entries. If index is zero, the value written to *pprec points to the first tagged item. If index is the value returned by the previous ps_cettac the value written to *pprec is a pointer to the next tagged item. This method allows the extraction of the names of all tagged files, one by one. PS ORDER Set the file list order VOID ps_order(INT mode, INT reverse); Set the current file name list order and sort the entries. The value of mode should be one of: PSEL_ORDER_NAME order alphabetically by full name (the default) PSEL_ORDER_TIME order by time of creation PSEL_ORDER_DATE order by date of creation PSEL_ORDER_SIZE order by size of file PSEL_ORDER_EXT order alphabetically by extension If reverse is TRUE the ordering is reversed. The current file name list is regenerated in the new order. If the ordering is not by extension the reordering will be completed before this method returns. Otherwise the method starts a build of the file name list in the specified order and this will complete at some future time. In either case an ps_NEW_LIST message is sent when the list is complete. All subsequent builds of the file name list will be performed in the specified order, until it is changed by a further ps_oRDER message. By default the file name list is built in ascending alphabetical name order. 15 - 10 15 FILE LISTS Deferred PSEL methods & PS NEW _LIST Process the completion of list building VOID ps_new_list (VOID) ; A deferred method which is always received on completion of any request to build or reorder the node and/or file name arrays. It may be received either before the build request returns or at a later time, depending on whether any lists need to be rebuilt. The supplier of this method is expected to test psel.blderr to determine the result of the build, and may also read psel.flags, psel.pfile, psel.pdir, psel.dirent, psel.fck and psel.fspec, aS explained in the descriptions of the property fields. A typical action would be to regenerate the data displayed in the user interface. It should examine psel.flags and: e if PSEL_RESET_DIR_ARRAY iS FALSE, regenerate the display of the directory/node names from the psel.pdir array e if PSEL_RESET_FILE_ARRAY iS FALSE, regenerate the display of the file names from the psel.pfile array e if PSEL_RESET_FSPEC iS FALSE, regenerate the display of the file name specification from the psel.fspec buffer This method is expected to return. Any code that may result in p_leave being called must be run under the protection of p_enter. 15-11 CHAPTER 16 FILE MANAGEMENT CLASSES The classes described in this chapter, and in particular the rman class, supply the basic engine for performing file management. They provide a set of high-level file system operations, for example, to copy a set of files, delete a directory structure or format an SSD. FMAN creates and uses component instances of the FMMK, FMFMT, FMSCAN, FMSRC and FMTARG active object classes (which are all subclasses of FACTIVE). These components do the bulk of the work and, by breaking a potentially lengthy operation into small sections, ensure that the application remains responsive to window server events. Note that only one of these components is active at any one time. The classes are strongly interdependent. rman reads from and writes to the property of the other classes, which themselves rely on their being owned components of Fuan. Precursors The reader is assumed to understand: e the active object scheduling mechanisms in the application manager e the FACTIVE, Fscan and Fcasy classes e the PLIB file system services e =the p_enter and p_leave error handling services Class diagram tee ¢ active / a me = ie factive cee AES “y ez ) -, aa ae _ to —~ ze 7 ae ) wo —— Te ie rea Gas M t Gataee Se fmscan / = ) err 16-1 OLIB REFERENCE FMAN srcfile targfile fmscan fmmk fmfmt action mode fman_init fman_cancel fman_copy srclen dispinfo targname wildsrcname wildtargname buf fman_name fman_info fman_attrib fman_delete fman_rename fman_complete fman_make fman_newname fman_remove fman_update fman_copydev fman_fileexist fman_format fman_error The rman (file manager) abstract class provides a set of file management operations. It must be subclassed to supply the deferred methods (which are chosen to provide a flexible interaction with any user interface) in order to create a useful object. It is not expected that a subclass will replace any of the supplied methods. An FMaN operation is initiated by sending the appropriate message from the following list: FMAN_COPY copy files FMAN_DELETE delete files FMAN_RENAME rename files FMAN_MAKE make a directory FMAN_REMOVE remove a directory (and its subdirectories) FMAN_COPYDEV copy a device FMAN_FORMAT format a device FMAN_NAME name a device FMAN_ATTRIB set file attributes Each of these methods will call p_1eave on error. If successful in initiating the required action, some of these methods return zero, while others call p_1eave (0) (to simplify the centralisation of error handling). It is therefore essential to send these messages under the protection of a p_enter (you could, for example, send the message by means of p_entersena). Under such protection the methods which call p_1eave (0) will behave equivalently to those methods which return zero. Any operation may complete before the send of the initialising message has returned. Normally, however, each of these operations will involve at least one active object (typically rmscan) which at some future time will be sent at least one ao_run message. Thus the operation will usually complete long after the send of the initiating message has returned. Regardless of when it occurs, the completion may represent the successful conclusion of the operation, or may result from the operation being cancelled, either at the user's request or as the result of an error condition. Every completion, for whatever reason, results in the subclass of rman receiving one, and only one, FMAN_COMPLETE message. This is typically used to destroy any visual indicator of the current operation that is being presented to the user. 16-2 Class definition 16 FILE MANAGEMENT CLASSES Defined in sub-category file factive.cl (generated header file factive.g). CLASS fman root The File Manager { ADD fman_init Create/Init the active objects ADD fman_cancel Cancel the last request ADD fman_copy Copy files ADD fman_delete Delete files ADD fman_rename Rename files ADD fman_make Make a directory ADD fman_remove Remove a dir structure ADD fman_copydev Copy a device ADD fman_format Format a device ADD fman_name Name a Pack ADD fman_info=p_dummy For define of O_FMAN_INFO at least ADD fman_attrib Set file attributes DEFER fman_complete Operation now complete DEFER fman_newname Operation now on this file(s) DEFER fman_update Update copying display DEFER fman_fileexist Destination file exists DEFER fman_error Error in operation, abort/continue CONSTANTS { FMAN_READ_SIZE 0x800 Copy 2k at a time FMAN_COPYING 0 FMAN_DELETE 1 FMAN_RENAME 2 FMAN_MAKE 3 FMAN_REMOVE 4 FMAN_FORMAT 5 FMAN_NAME 6 FMAN_ATTRIB 7) } PROPERTY 5 { PR_FMSRC *srcfile; Src/Read file active object PR_FMTARG *targfile; Target/Write file active object PR_FMSCAN *fmscan; File system scan active object PR_FMMK *fmmk; Make Dir active object PR_FMFMT *fmfmt; Format a pack. UWORD action; Current file manager action UWORD mode; Current file create/replace mode UWORD srclen; FCOPY_DISP dispinfo; Display info UBYTE targname [P_FNAMESIZE]; Fully spec'ted target name UBYTE wildsrcname[P_FNAMESIZE]; Entered wildcarded srcname UBYTE wildtargname[P_FNAMESIZE]; Entered wildcarded target UBYTE buf [FMAN_READ_SIZE]; } } Property fman.srcfile the handle of an instance of rusrc, representing the source file during a file copy. It should not be accessed by any subclass. fman.targfile the handle of an instance of rmtare, representing the target file during a file copy. It should not be accessed by any subclass. fman. fmscan the handle of an instance of rmscan, used to generate the lists of files upon which an operation acts. It should not be accessed by any subclass. fman. fmmk the handle of an instance of rmmx, used to create a directory structure. It should not be accessed by any subclass. fman. £mfmt the handle of an instance of rvrut, used to format an SSD. It should not be accessed by any subclass. 16 -3 OLIB REFERENCE fman.action the current file manager action, used to determine what operation to perform on a name generated by rmscan. It takes one of the following values: FMAN_COPYING - copying files FMAN_DELETE - deleting files FMAN_RENAME - we are renaming files FMAN_ATTRIB - Changing the file attributes FMAN_MAKE - creating a directory structure FMAN_REMOVE - removing a directory structure FMAN_FORMAT - formatting an SSD FMAN_NAME - naming an SSD It should not be accessed by any subclass, but see also fman.dispinfo, which also contains this data. fman.mode the current create/replace mode for a file copy operation, At the start of a copy it is set to create the copy file(s) but may later be modified, depending on the result of an rMaN_FILEEXxIsT message. It should not be accessed by any subclass. fman.srclen how many bytes to read from a source file, and the length of buffered data to write to a target file. It should not be accessed by any subclass. fman.dispinfo information describing the current file manager action (see the fman_newname method for details of the rcopy_p1sp struct). It is intended to be used to provide information about the current operation for the user interface. It should only be read from within the fman_newname method. fman.targname the generated full file specification of a target file. It should not be accessed by any subclass. fman.wildsrcname the wildcarded file name passed as the source file name in one of the messages that initiates an operation. It should not be accessed by any subclass. fman.wildtargname the wildcarded file name passed as the target file name in one of the messages that initiates an operation between two files. It should not be accessed by any subclass. fman.buf the data read from the source file and written to the target file during a file copy. It should not be accessed by any subclass. FMAN methods FMAN_INIT Initialise the file manager VOID fman_init (VOID) ; Create and initialise the five active objects whose handles are stored in the first five items of rman's property. The initialisation of each registers rman as its owner and adds the active object to the application manager's active object task queue. Calls p_1leave (E_GEN_NOMEMoRy) on failure. FMAN_CANCEL Cancel an outstanding request VOID fman_cancel (VOID) ; Cancel any current file operation, sending ao_cANCEL messages to the appropriate active object components, depending on the value of fman. action. Following this, an FMAN_COMPLETE message Is sent to indicate that the current operation has been completed. 16-4 16 FILE MANAGEMENT CLASSES FMAN_COPY Copy files VOID fman_copy(UBYTE *src, UBYTE *targ, INT flags); Start the copy of files specified by the wildcarded source file name at src to the wildcarded target name at targ. Before the copy is started, the source and target file names are validated: e The source file name must not be a null string and it must be a name acceptable to the p_fparse function (the name is parsed with "*" as the related name and the resulting full file specification is written into fman.wildsrcname). e If the target name is a null string, it is replaced by the file name and extension taken from the full file specification in fman.wildsrcname. The target name is parsed with a nux related name into fman.wildtargname. A final check is made that the resulting source and target file names are not identical. If the validation generated the target name from the source name (because targ points to a null string) the name and extension in fman.wildtargname are replaced by "*". Following this, fman.dispinfo.protocol and fman.action are both set to rman_copyinec and fman.dispinfo.blksiz iS set tO FMAN_READSIZE, With fman.dispinfo. flags set to indicate that the appropriate fields are valid. The value of fman.mode 1s Set to P_FCREATE|P_FSTREAM, SO that, initially, the copy files will be created (as opposed to, say, replacing existing files). The flags parameter should contain a bitwise combination of one or more of the following values, defined in factive. g: FS_ALL_FILES include all non-directory files that are neither hidden nor system files FS_WRITABLE aS FS_ALL_FILES, but excludes write-protected files (see below) FS_HIDDEN include hidden files FS_SYSTEM include system files FS_DIRECTORIES include directory files FS_MODIFIED exclude unmodified files (include only files that have p_ramop set) FS_INCLUDE_SUBDIRECTORIES extend the scan to subdirectories FS_PARSE_NAME parse file names before reporting them The rmscan component has fscan. flags set to the passed flags value, ored with rs_HIDDEN|FS_SYSTEM, and fscan.match is set to point to the name and extension within the fman.wildsrcname buffer. An FS_FSCAN message is then sent to the rmscan component to generate the list of files to copy. The initial start up file name passed with this message is the full contents of fman.wildsrcname. Any error encountered during the file name validation or in rMscan's rs_Fscan method results in p_leave being called with an appropriate error. On successful completion the method calls p_leave (0). The rman_copy message must therefore be sent under the protection of a p_enter. Further processing of the file copy is handled by the rmscan component. FMAN_DELETE Delete files VOID fman_delete(UBYTE *src, INT flags); Start the deletion of one or more files as specified by the wildcarded file specification string pointed to by src. In contrast to the fman_remove method, directories are not deleted. Before the deletion is started the file specification string is validated. It must not be a null string and it must be a name acceptable to the p_fparse function (the name is parsed with "«" as the related name and the resulting full file specification is written into fman.wildsrcname). Following this, fman.dispinfo.protocol and fman.action are both set to rMaN_DELETE. The value of fman.dispinfo. flags is Set to indicate that the appropriate field is valid. 16-5 OLIB REFERENCE The flags parameter should contain a bitwise combination of one or more of the following values, defined in factive. g: FS_ALL_FILES include all non-directory files that are neither hidden nor system files FS_WRITABLE aS FS_ALL_FILES, but excludes write-protected files (see below) FS_HIDDEN include hidden files FS_SYSTEM include system files FS_DIRECTORIES include directory files FS_MODIFIED exclude unmodified files (include only files that have p_ramop set) FS_INCLUDE_SUBDIRECTORIES extend the scan to subdirectories FS_PARSE_NAME parse file names before reporting them The rmscan component has fscan. flags set to the passed flags value, ored with rs_HIDDEN|FS_SYSTEM, and fscan.match is set to point to the name and extension within the fman.wildsrcname buffer. An FS_FSCAN message is then sent to the rmscan component to generate the list of files to delete. The initial start up file name passed with this message is the full contents of fman.wildsrcname. Any error encountered during the file name validation or in rmscan's rs_Fscan method results in p_leave being called with an appropriate error. On successful completion, the method calls p_leave (0). The rMaAN_DELETE message must therefore be sent under the protection of a p_enter. Further processing of the file deletion is handled by the rmscan component. FMAN_RENAME Rename files VOID fman_rename (UBYTE *src, UBYTE *targ); Start the renaming of one or more files as specified by the wildcarded file specification string pointed to by src to names specified by the wildcarded file specification string pointed to by targ. Before the rename is started the source and target file names are validated: e The source file name must not be a null string and it must be a name acceptable to the p_fparse function (the name is parsed with "*" as the related name and the resulting full file specification is written into fman.wildsrcname). e If the target name is a null string it is replaced by the file name and extension taken from the full file specification in fman.wildsrcname. The target name is parsed with a nut related name into fman.wildtargname. Final checks are made that the resulting source and target file names are not identical, and that both file specifications refer to the same directory (files can not be renamed across directories, devices or file systems). If the validation generated the target name from the source name (because targ points to a null string), the name and extension in fman.wildtargname are replaced by "*". Following this, fman.dispinfo.protocol and fman.action are both set to FMAN_RENAME. The value of fman.dispinfo. flags is set to indicate that the appropriate field is valid. The rmscan component has fscan. flags Set t0 FS_ALL_FILES|FS_HIDDEN|FS_sysTEM and fscan.match IS set to point to the name and extension within the fman.wildsrcname buffer. An rs_rscan message is then sent to the rmscan component to generate the list of files to rename. The initial start up file name passed with this message is the full contents of fman.wildsrcname. Any error encountered during the file name validation or in rmscan's rs_Fscan method results in p_leave being called with an appropriate error. On successful completion the method calls p_1eave (0). The rMaN_RENAME message must therefore be sent under the protection of a p_enter. Further processing of the file rename is handled by the rmscan component. 16 - 6 16 FILE MANAGEMENT CLASSES FMAN_MAKE Make a directory tree INT fman_make(UBYTE *name) ; Start the creation of a directory or directory structure as specified by name (using p_mkdir). The values of fman.dispinfo.protocol and fman.action are both set to rman_maxe. The string pointed to by name is copied into the fman.targname buffer and fman.dispinfo.sfname is set to point to this buffer. The value of fman.dispinfo. flags Is set to indicate that the appropriate fields are valid. Following this, the property of the rmmx component is modified directly, setting active.isactive tO TRUE, and active.stat to zero. The I/O semaphore is then signalled (by calling p_iosigna1) so that rumx will, at some future time, receive an ao_RUN message. The method returns zero. See the rmx class for details of further processing. FMAN_REMOVE Delete a directory structure INT fman_remove (UBYTE *name) ; Remove the directory structure specified by the name of a directory pointed to by name. The specified directory and all included files and subdirectories are deleted. The values of fman.dispinfo.protocol and fman.action are both set to rman_remove. The value of fman.dispinfo. flags is set to indicate that the appropriate field is valid. The passed name is parsed into the fman.wildsrcname buffer and adjusted, if necessary (with the aid of a call to p_chdir) to ensure that it contains a valid directory name for the relevant filing system. (For MSDOS, for example, it ensures that the directory name includes a trailing '\' character.) This may fail if the supplied text does not produce a valid directory name. The rmscan component has fmscan.match Set to point to the string "*" (to match all files) and fmscan. flags Set tO FS_ALL_FILES|FS_INCLUDE_SUBDIRECTORIES |FS_HIDDEN|FS_SYSTEM So that it will generate the names of all the files and directories below the specified directory. Following this, the rmscan component is sent an rs_Fscan message. The initial start up file name passed with this message is the full contents of fman.wildsrcname. Returns zero if the remove has been started successfully, or calls p_leave on error. Further processing of the directory removal is handled by the rmscan component. FMAN_COPYDEV Copy a device VOID fman_copydev(UBYTE *src, UBYTE *targ, INT flags); Initiate the copy of the device specified by src to the device and directory name specified by targ, reproducing the source device structure under the target directory. This method effectively provides a backup service. The results will be unpredictable if src does not point to a device name. The value of f1ags should be either zero, to copy all files, or rs_MopDIFIED, to restrict the copy to only those files which are marked as modified. The values of fman.dispinfo.protocol and fman. action are both set to rMAN_coPyYING, and fman.dispinfo.blksiz iS set to FMAN_READ_S1ZE. The value of fman.dispinfo. flags iS set to indicate that the appropriate fields are valid. The target name is copied into fman.wildtargname, and is converted to a directory name, as in the fman_remove method. The source name is parsed into fman.wildsrcname and both names are validated, as for the fman_copy method. A further check ensures that the source and target names do not specify the same device. The value of fman.mode 1s Set to P_FREPACE | P_FSTREAM, SO any previously existing target files will be overwritten without notification. The rmscan component has fscan. flags Set to the passed flags value, ored with FS_ALL_FILES|FS_INCLUDE_SUBDIRECTORIES|FS_HIDDEN|FS_SYSTEM, and fscan.match Is Set to point to the string "*". An rs_Fscan message Is then sent to the rmscan component to generate the list of files to copy. The initial start up file name passed with this message is the full contents of fman.wildsrcname. Any error encountered during the name validation or in rmscan's Fs_Fscan method results in p_leave being called with an appropriate error. 16-7 OLIB REFERENCE On successful completion, the method calls p_1eave (0). The rman_copypEv message must, therefore, be sent under the protection of a p_enter. Further processing of the device copy is handled by the rmscan component. FMAN_FORMAT Format a device INT fman_format (UBYTE *devname, UBYTE *volname, UINT flags); Initiate the formatting of the device specified by devname, giving it a volume name specified by volname. Although there is no intrinsic restriction on the filing system in which the format is to take place, the REM:: filing system currently does not support the required formatting services. Thus, an error condition will occur if an attempt is made to format a device on the REM:: filing system. The value of flags must be either zero or p_rFLowDENstITv. It specifies the format mode for the formatting of floppy disks. (This is primarily supplied to support future expansion since, at the time of writing, there is no LOC:: floppy disk drive.) The passed device name and volume name concatenated into fman.targname and the device is opened for formatting with the open file handle written directly to the active.pcb property of the rmrmT component. The values of fman.dispinfo.protocol and fman.action are both set to FMAN_FORMAT, fman.dispinfo.sfname is set to point to the fman.targname buffer and fman.dispinfo.blksiz Is set to 1. The value of fman.dispinfo. flags Is set to indicate that the appropriate fields are valid. The property of the rurmt component is further modified, by setting fmfmt . flags to FMFMT_NaME (defined by the rmrmt class) active.isactive to TRUE and active.stat to zero. The I/O semaphore is signalled by a call to p_iosignal, ensuring that rmrut will eventually receive an ao_RUN message. Returns zero if the remove has been started successfully, or calls p_leave on error. See the rmrmt class for details of further processing. FMAN_NAME Name a device INT fman_name(UBYTE *devname, UBYTE *volname) ; Change the volume name on the device specified by devname to the name pointed to by voiname. This method is exceptional, in that the action is performed synchronously. It will always have completed (either successfully or with an error) by the time the method terminates. The method returns zero if the device was named successfully, or calls p_1eave with the appropriate error number on failure. FMAN_INFO Obsolete method VOID fman_info (VOID) ; This method is no longer in use and is maintained solely to preserve the OLIB user interface. FMAN_ATTRIB Set file attributes VOID fman_attrib(UBYTE *pname, UWORD attribs, UINT flags); Initiate the setting of the file attributes specified by att ribs for the set of files specified by the wildcarded name pointed to by pname. The value of attribs may take any combination of the following flags: P_FAWRITE a writable file (not read only, deletable) P_FAHIDDEN a hidden file P_FASYSTEM a system file P_FAMOD the file is marked as modified. Both the set and the clear states of all flags are significant; the clear state has the opposite meaning to the set state. For example, if the p_rawr1te flag is not set then the file will be made read only. 16-8 16 FILE MANAGEMENT CLASSES The value of f1ags must be either nuLt to restrict the operation to the single specified directory, or FS_INCLUDE_SUBDIRECTORIES to extend the setting of attributes to files within all subdirectories of the specified directory. The values of fman.dispinfo.protocol and fman.action are both set to rMAN_ATTRIB and fman.dispinfo. flags is set to indicate that the appropriate field is valid. The name pointed to by pname must not be a null string. Provided it passes this check, it is parsed (with a related name of "*") into £man.wildsrcname, and fman.mode is Set to the value of attribs. The rmscan component has fscan. flags Set to the passed flags value, ored with FS_ALL_FILES|FS_HIDDEN|FS_SYSTEM, and fscan.match Is Set to point to the name and extension within the fman.wildsrcname buffer. An rs_rscan message is then sent to the rmscan component to generate the list of files whose attributes are to be changed. The initial start up file name passed with this message is the full contents of fman.wildsrcname. Any error encountered during the name validation or in rmscan's Fs_Fscan method results in p_leave being called with an appropriate error. On successful completion the method calls p_1eave (0). The rMaN_ATTRIB message must therefore be sent under the protection of a p_enter. Further processing of the setting of attributes is handled by the ruscan component. Deferred FMAN methods & FMAN_COMPLETE Processing completed VOID fman_complete (VOID) ; Notify that an asynchronous file manager request has now completed. This message will not be received if the request failed to start (if, for example, the fman_copy method called p_leave with a non-zero parameter). Provided the request started successfully, this message is guaranteed to be received, regardless of the reason for the completion. Such reasons include: e there are no more files to handle e the request was aborted by the user, or otherwise cancelled e the request terminated with a non-recoverable error Errors in the file manager request are handled (for example, via the FMAN_ERROR and FMAN_FILEEXIST messages) prior to receipt of this message and so the fman_complete method does not need an associated completion status. Note that it is not considered an error if the requested action does not actually process any files, such as in a request to delete files from an empty directory. If such a condition needs to be reported, it may be detected by the receipt of an FMAN_COMPLETE message that is not preceded by one or more FMAN_NEWNAME messages. Typically, a subclass would use the receipt of this message to destroy any objects associated with the display of status information for the current operation and to allow further file management actions be selected. This method is expected to return. Any code that may result in p_leave being called must be run under the protection of p_enter. 16-9 OLIB REFERENCE & FMAN_NEWNAME Processing a new file VOID fman_newname(FCOPY_DISP *pinfo); Notify that processing is about to commence on a new file. Information about the file is contained in the rcopy_pisp struct pointed to by pinfo. This struct is declared in the rcasy class, since it contains data that is used by a number of rcasy subclasses. For convenience it is reproduced here: typedef struct { UWORD flags; Which fields are valid UWORD protocol; Which protocol being used UWORD blksiz; Size of each block UWORD blkno; Current block number UWORD sftype; Source file type UWORD rftype; Receive file type ULONG sfsize; Source file size ULONG rfsize; Receive file size ULONG sfdate; Source file last modification date ULONG rfdate; Receive file last modification date UBYTE *sfname; Source file name UBYTE *rfname; Receive file name } FCOPY_DISP; Since, in general, not all fields contain valid information, the f1ags field indicates which of the fields are valid as follows: FCOPY_DISP_SFTYPE sftype contains either p_FTEXT Or P_FSTREAM FCOPY_DISP_SFSIZE sfsize contains the source file size, in bytes FCOPY_DISP_SFDATE sfdate contains, as a system time, the last modification date of the source file FCOPY_DISP_SFNAME sfname contains a pointer to the source file name as a zero terminated string FCOPY_DISP_RFTYPE rftype contains either p_FTEXT Or P_FSTREAM FCOPY_DISP_RFSIZE rfsize contains the remote or target file size in bytes FCOPY_DISP_RFDATE rfdate contains, as a system time, the last modification date of the remote or target file FCOPY_DISP_RFNAME rfname contains a pointer to the remote or target file name as a zero terminated string FCOPY_DISP_BLKSIZ blksiz contains a value that represents the amount of data that has been processed when each rMaN_UPDATE message is received FCOPY_DISP_BLKNO blkno contains a value specifying the current block number that is being processed (provided the appropriate fields are valid, the value of sfsize/blksiz equals the maximum value of b1kno that will be reached during the processing) FCOPY_DISP_PROTOCOL protocol contains a value specifying the current rman action (one of FMAN_COPYING, FMAN_DELETE, FMAN_RENAME, FMAN_MAKE, FMAN_REMOVE, FMAN_FORMAT, FMAN_NAME Of FMAN_ATTRIB) Irrespective of the contents of the flags field, the contents should not be read outside the fman_newname method. An FMAN_NEWNAME message is sometimes sent even if an error has prevented one or more fields from being set up. It is therefore essential always to check if a field is valid before using its contents. This method has two principal uses: e it provides information about the current stage of processing that may be presented to the user e it supplies a context for any particular error (for example, if a file is read only, it is known in advance that the p_delete service will fail with an E_rFILE_RDONLY error). This method is expected to return. Any code that may result in p_1leave being called must be run under the protection of p_enter. 16 - 10 16 FILE MANAGEMENT CLASSES & FMAN_UPDATE Section of processing completed VOID fman_update (VOID) ; Notify that the next section of the requested action has completed. The message will only be received during the processing of the copy and format services, each time that either a block of rmaN_READ_S1zE bytes has been copied, or that a section of the pack has been formatted. This method may be used to update any progress report that is being presented to the user. The values of fman.dispinfo.sfsize and fman.dispinfo.blksiz can be used to determine the number of times that an FMAN_UPDATE message will be received, in order to report a percentage completion to the user. Note that when formatting (that is, when fman.dispinfo.protocol has the value rmMan_rormat) only the low 16 bits of fman.dispinfo.sfsize contain valid data, so in this case the top 16 bits should be masked off. This method is expected to return. Any code that may result in p_leave being called must be run under the protection of p_enter. & FMAN_FILEEXIST File exists INT fman_fileexist (VOID); Notify that the destination file of a copy exists. This message will only be received only if fman.mode is not set to cause existing files to be replaced. It can therefore only be received if files are being copied as a result of an rMaN_copy message (and not FMAN_COPYDEV) Since the fman_copydev method sets fman.mode to include the p_rREPLAcE flag. The method is expected to return one of the following values: -1 (or any other negative value) abandon the copy service 0 replace this file only, further clashing names will cause this message to be received again 1 __ skip this file and continues with the next file 2 replace this file and all subsequent files, regardless of whether the destination file exists. This method is expected to return. Any code that may result in p_1eave being called must be run under the protection of p_enter. & FMAN_ERROR Determine error response INT fman_error(INT errnum) ; Determine the action to be taken in response to a detected error with error number errnum. The method should return a value which specifies the form of error recovery. The range of options is different, depending on whether or not the current operation is a file copy (gman. action is FMAN_COPYING). During a file copy the return value may be one of: 0 abandon copying the current file and continue with the following file 1 (or any non-zero value) abandon the entire copy operation. During any other operation the return value may be one of: 0 retry the operation on the current file 1 abandon the entire operation 2 abandon the operation on the current file and continue with the following file. This method is expected to return. Any code that may result in p_1leave being called must be run under the protection of p_enter. 16-11 OLIB REFERENCE FMMK ACTIVE FACTIVE FMMK gq owner priority isactive pcb stat destroy fa_close ao_run ao_init ao_cancel ao_abrun ao_queue See The rmx class is designed to be used as a component of rman which uses it to implement the make directory service. Since the supplied ao_run method contains assumptions about the property of the object whose handle is stored in factive.owner, it is not suitable for use other than as a component of rman. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fmmk factive The 'Create directory' active object { REPLACE ao_run } Property None. FMMK methods AO_RUN Process a make directory request INT ao_run(VOID) ; Make the directory that was specified by an earlier rmMaN_MAKE message. Sends the owning FMAN an FMAN_NEWNAME Message, passing the address of rman's fman.dispinfo struct. Then makes the directory (and any necessary intermediate directories) with a call to p_mkdir. If this call returns an error, FMAN is sent an FMAN_ERROR message and, if this message returns FaLsz, a further attempt is made to create the directory. This is repeated until either the directory creation succeeds or the FMAN_ERROR message returns a non-zero value. Following this, rman is sent an FMAN_COMPLETE message to indicate that the processing is complete. Note that the ac_run method is executed only once for each rman_MaAKE message. The method returns RUN_ACTIVE_USED. 16 - 12 16 FILE MANAGEMENT CLASSES FMFMT ACTIVE FACTIVE q owner rdword priority rdlen isactive flags pcb stat destroy fa_close ao_run aorinit ao_init ao_abrun aereanecet | ao_cancel ao_queue aeTFuAn The rurnr class is designed to be used as a component of rman, which uses it to implement the format service. Since the supplied methods contain assumptions that the object whose handle is stored in fact ive.owner is an instance of the rman class, it is not suitable for use in other situations. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fmfmt factive Format active object { REPLACE ao_run REPLACE ao_abrun CONSTANTS { FMFMT_NAME 0x01 } PROPERTY { UWORD rdword; UWORD rdlen; UWORD flags; Controlling flags } } Property fmfmt .rdword a scratch buffer to receive data read during the format process. fmfmt .rdlen the number (two!) of bytes read into tmfmt .rdword fmfmt .flags controlling flag, initially set to rmmx_Nname and cleared on the first pass through the ao_run method FMFMT methods AO_RUN Process a format request INT ao_run(VOID); Perform a segment of the processing of a file manager format request. If this is the first time that the method has been called following a rman_rormat message, the first read is made, to obtain the format count, and the result is written to the bottom 16 bits of rman's fman.dispinfo.sfsize. Following this, rman is sent an FMAN_NEWNAME message, passing the address of FMAN'S fman.dispinfo struct. 16 - 13 OLIB REFERENCE On subsequent calls, unless an error condition is encountered, rman is sent an FMAN_UPDATE Message, active.stat is set to TRUE and another read is queued. If a read request completed with an &_FILE_koF error, indicating that the format is complete, the method sends itself an ra_cLOsE message and then sends rMaN an FMAN_COMPLETE Message. All other errors result in p_1eave being called. The method returns RUN_ACTIVE_USED. AO_ABRUN Process formatting error VOID ao_abrun (VOID) ; Process an error which caused the ao_run method to call p_ieave. Supersends an AO_ABRUN message and then sends rman an FMAN_COMPLETE message. FMSCAN ACTIVE FACTIVE FSCAN FMSCAN q owner priority isactive pcb stat destroy fa—etese fs_matchname ao_abrun ao_init fs_fscan fs_filename ao_cancel ao_queue fs_dirname aorebeun ao_run fs_end_dirlist fa_close fs_fscan_end The rmscan class is designed to be used as a component of rman, which uses it to implement the copy file, delete file, rename file, remove directory and copy device services. Since the supplied methods contain assumptions that the object whose handle is stored in fact ive.owner is an instance of the rman class, it is not suitable for use in other situations. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fmscan fscan File system scan active object { REPLACE ao_abrun REPLACE fs_filename REPLACE fs_dirname REPLACE fs_end_dirlist REPLACE fs_fscan_end } Property None. 16 - 14 16 FILE MANAGEMENT CLASSES FMSCAN methods AO_ABRUN Process an error VOID ao_abrun (VOID); Handle any error that arises within the ao_run method (provided by rscan). Reads the owning rman's property and, if man. action iS FMAN_COPYING, sends Ao_CANCEL messages to the fman.srcfile and fman.targfile component objects. Then supersends the ao_aprun message and sends rMaAN an FMAN_COMPLETE message. FS_ FILENAME Next file name VOID fs_filename (VOID) ; Process a new file name during a file name scan (the full file specification of the file is in the fscan.name buffer). This method will be called during a copy file, copy device, delete file, delete directory, rename file or set attribute service. Reads the owning rmwan's property and performs one of the following actions, depending on the value of fman.action: FMAN_COPYING the name for the copy file is generated by merging the current file name from the scan with the wildcarded target name (from fman.wildtargname). If this is successful, the source and target files are opened by sending FC_OPEN messages to Fan's FMsRc and rmTarG components. The source file is opened in read only mode (with the p_rsuare flag set) and the target file in text (P_FSTREAM_TEXT) or binary mode, according to the source file type specified by fman.mode. Regardless of the success or failure of the name generation or the attempt to open the files, rman is sent an FMAN_NEWNAME message, passing the address of the fman.dispinfo struct (whose sfname and rfname elements point respectively to the source and target file names). This allows the user interface to display names that cause an error in addition to displaying valid names. The copy is started by sending an ao_QuEUE message to FMAN'S FMSRC component. Further processing is controlled by the rmsrc and rMTaRG components. FMAN_RENAME the file's new name is generated by merging the current file name from the scan and the wildcarded target name (from fman.wildtargname) and FMAN is sent an FMAN_NEWNAME message, passing the address of the fman.dispinfo struct (whose sfname and rfname elements point respectively to the old and new file names). Provided the name generation did not fail, the file is renamed. FMAN_ATTRIB sends FMAN an FMAN_NEWNAME message and sets the file's attributes according to the value of fman.mode. FMAN_DELETE or sends FMAN an FMAN_NEWNAME message and deletes the file. FMAN_REMOVE An £_FILE_EXxIsT error that occurs when trying to create the target file for a copy causes rman to be sent an FMAN_FILEEXIST message. See the earlier description of this method for the range of possible return values and the resulting actions. All other errors cause an FMAN_ERROR message to be sent to rman and the return value determines what form of error recovery action is taken. The range of possible actions depends on the service that is being processed. The various options are listed in the description of the fman_error method. Except when either copying files or, following an error, the user selects to abandon the entire operation, the scan is continued by sending rscan an Ao_QUEUE. 16 - 15 OLIB REFERENCE FS _DIRNAME Next directory name VOID fs_dirname (VOID) ; Process a new directory file during a file name scan. This method will be called when scanning into a subdirectory during a copy or delete service. Reads the owning Fan's property and, if fman. action 1S FMAN_COPYING, adjusts the contents of the fman.wildtargname buffer to refer to the new subdirectory. Any error here results in p_leave being called. Sends itself an Aao_QUEUE message to continue the scan. FS _END_DIRLIST End of subdirectory VOID fs_end_dirlist (VOID); Process the reaching of the end of a subdirectory during a file name scan. Reads the owning rvan's property and, if fman.action 18 FMAN_REMOVE, deletes the subdirectory whose name is specified in the fman.name buffer (any files contained in the subdirectory have been deleted earlier in the scan). Any error causes rman to be sent an FMAN_ERROR message. The return value determines the action as follows: 0 retry the deletion until it succeeds, or the user decides to skip or abandon 1 abandon the entire operation - send rmscan an AO_CANCEL message and FMAN an FMAN_COMPLETE message 2 continue, without deleting the subdirectory. Otherwise, if man. action 1S FMAN_COPYING, adjusts the contents of the fman.wildtargname buffer to refer to the parent directory. Any error here results in p_leave being called. Sends itself an ao_quEUE message to continue the scan except following a user decision to abort. FS FSCAN_END Scan completion VOID fs_fscan_end(VOID); Process the end of the file name scan. Reads the owning rman's property and, if man. action is FMAN_REMOVE, deletes the directory whose name is specified in the fman.name buffer (any files and subdirectories have been deleted during the scan). If fman.name specifies the root directory, the attempt to delete it will, of course, fail but this is not considered an error. Any other error causes rman to be sent an FMAN_ERROR message. The return value determines the action as follows: 0 retry the deletion until it succeeds, or the user decides to skip or abandon 1 abort the entire operation - send rMscan an AO_CANCEL message and send rMaN an FMAN_COMPLETE message 2 continue, without deleting the directory. Sends rvan an FMAN_COMPLETE message. 16 - 16 16 FILE MANAGEMENT CLASSES FMSRC ACTIVE FACTIVE FCASY q owner recvname priority flags isactive pcb stat destroy #a—ctese ao_cancel fc_request_comp ao_init ao_run 2 + ao_queue ao_abrun fa_close fc_write fc_open The rsrc class is designed to be used as a component of rman which uses it to implement the reading of the source file in the copy file service. Since the supplied method contains assumptions that the object whose handle is stored in fact ive.owner is an instance of the rman class, it is not suitable for use in other situations. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fmsrc fcasy Source file active object { REPLACE fc_request_comp } Property None. FMSRC methods FC_REQUEST COMP Process a read completion VOID fc_request_comp (VOID) ; Process the completion of a read from the source file. The read will always have been of rman_READ_S1zE bytes into rman's fman.buf buffer. On completion of the read fman.srcien contains the number of bytes actually read. If the read completed successfully, it sends an rc_wRITE message to FMAN's FMTARG component to write fman.srclen bytes from fman.buf to the destination file. If the read completed with an z_F1Lz_k£oF error, the copy of the file is complete. rman's FMTARG component is sent an FA_CLOSE message, the destination file is set to have the attributes and modification date of the source file (an error in reading the attributes of the source file causes p_leave to be called). If the copy is of modified files only, the modified (p_ramop) attribute of the source file is cleared. An ao_QUEUE message is sent to FMAN's FSCAN component to continue the scan for another file to copy. If the read completed with any other error, rman's FMTARG Component is sent an Ao_CANCEL message and FMAN is Sent an FMAN_ERROR Message, which allows the user to skip this file and continue, or abort the copy service. 16-17 OLIB REFERENCE FMTARG ACTIVE FACTIVE FCASY FMTARG q owner recvname priority flags isactive pcb stat destroy #a—ctese ao_cancel fc_request_comp ao_init ao_run ae-ean ao_queue ao_abrun fa_close fc_write fc_open The rmtare class is designed to be used as a component of rman, which uses it to implement the writing of the target file in the copy file service. Since the supplied method contains assumptions that the object whose handle is stored in fact ive.owner is an instance of the rman class, it is not suitable for use in other situations. Class definition Defined in sub-category file factive.cl (generated header file factive.g). CLASS fmtarg fcasy Target file active object { REPLACE fc_request_comp } Property None. FMTARG methods FC_REQUEST COMP Process a write completion VOID fc_request_comp (VOID); Process the completion of a write to the target file. If the write completed successfully, sends rman an FMAN_UPDATE message, updates the value of rman's fman.srclen tO FMAN_READ_SIZE and sends an Ao_QUEUE message to FMAN'S FMSRC Component. If the write completed with an error, rmMan's FMSRC Component is sent an AO_CANCEL message and FMaN is sent an FMAN_ERROR message, which allows the user to skip this file and continue or abandon the copy service. 16 - 18 16 FILE MANAGEMENT CLASSES File manager example The following example is of a simple application to back up the contents of M-:, using a console window as the user interface. It formats an SSD in B: and copies the entire contents of M: to B:. The process may be aborted at any time by pressing the ESC key. The category file, mcopy.cat, is as follows: IMAGE mcopy EXTERNAL olib INCLUDE appman.g INCLUDE factive.g INCLUDE p_keyb.h CLASS bakfman fman File manager to perform format and copydev operations { REPLACE fman_complete REPLACE fman_newname REPLACE fman_update REPLACE fman_error PROPERTY { UWORD count; UWORD increment; UWORD maxcount; } } CLASS breakkey active Looks for Esc key being pressed { REPLACE ao_init REPLACE ao_run REPLACE ao_queue PROPERTY { P_CON_KBREC k; } CLASS bakapp appman { REPLACE am_init PROPERTY 2 { PR_BAKFMAN *fman; PR_BREAKKEY *breakkey; } } Operation now complete Operation now on this file(s) Update copying display Error in operation, abandon/continue Note that no method function is supplied for the file manager's deferred fman_fileexist method. It will never be called in this example since the only file copies are ones that will replace any existing file of the Same name. 16 - 19 OLIB REFERENCE The code for the application is as follows. Note that pressing Esc during the format will leave the SSD in B: unformatted. #include #include #include #include #include #include #include GLREF_D PR_BAKAPP *w_am; GLREF_D VOID *winHandle; /* console device handle */ LOCAL_D INT escaped; #pragma save,METHOD_CALL METHOD VOID breakkey_ao_init (PR_BREAKKEY *self) { self—>active.priority=PRIORITY_ACTIVE_WSERV; self->active.pcb=winHandle; /* console device must already be open */ p_send3 (w_am, O_AM_ADD_TASK, self) ; p_send2 (self, O_AO_QUEUE) ; } METHOD VOID breakkey_ao_queue (PR_BREAKKEY *self) /* Checks for ESC during file manager commands */ { p_ioc4 (self-—>active.pcb, P_FREAD, &self->active.stat, &self-—>breakkey.k) ; self—>active.isactive=TRUE; } METHOD breakkey_ao_run(PR_BREAKKEY *self) /* Aborts if ESC pressed baw A { if (self->breakkey.k.keycode!=0x1b) p_send2 (self, O_AO_QUEUE) ; else { escaped=TRUE; p_printf("\r\nUser aborted") ; p_send2 (w_am->bakapp.fman,O_FMAN_CANCEL); /* stops any service cleanly at any time */ } return (RUN_ACTIVE_USED) ; } METHOD bakfman_fman_error(PR_BAKFMAN *self,INT error) /* An error was detected in the operation of the engine. This method causes the operation to be abandoned and, in this example, results in the application being terminated. */ { escaped=TRUE; return (1); } 16 - 20 16 FILE MANAGEMENT CLASSES METHOD VOID bakfman_fman_complete (PR_BAKFMAN *self) /* The last requested operation is now complete, for ANY reason. xf { if ((self->fman.action==FMAN_FORMAT) && (!escaped) ) p_printf("\r\n*** Format successfully completed ***\r\n"); p_send2 (w_am,O_AM_STOP) ; } #define FORMAT_FLAGS (FCOPY_DISP_BLKS1IZ|FCOPY_DISP_SFSIZE|FCOPY_DISP_SFNAME) METHOD VOID bakfman_fman_newname (PR_BAKFMAN *self,FCOPY_DISP *pinfo) /* A new file is being acted on by the engine. */ { if (self->fman.action==FMAN_COPYING) { if (pinfo->flags&FCOPY_DISP_SFNAME) p_printf ("Copying %s",pinfo->sfname) ; } if (self->fman.action==FMAN_FORMAT) { /* store some info to avoid accessing it outside this method */ self-—>bakfman.count=self->bakfman.maxcount=0; if ((pinfo->flags&FORMAT_FLAGS) ==FORMAT_FLAGS) { self—>bakfman.increment=pinfo->blksiz; self—>bakfman.maxcount=pinfo->sfsize; /* copy sfsize into UWORD since, for format, only lower 16 bits are valid */ p_printf ("Formatting %s",pinfo->sfname) ; } METHOD VOID bakfman_fman_update (PR_BAKFMAN *self) /* Update display, as next copy/format block has been processed. Does not report progress on the copying of files. af { if (self->fman.action==FMAN_FORMAT && self->bakfman.maxcount) { self->bakfman.count+=self->bakfman.increment; p_print ("\r%05u %05u", self—>bakfman. count, self->bakfman.maxcount) ; } } METHOD bakapp_am_init (PR_BAKAPP *self) { p_supersend3 (self,O_AM_INIT,FLG_APPMAN_CLEAN|FLG_APPMAN_ONLYONE) ; self—>bakapp.fman=f_newsend (CAT_MCOPY_MCOPY, C_BAKFMAN, O_FMAN_INIT) ; self—>bakapp.breakkey=f_newsend (CAT_MCOPY_MCOPY, C_BREAKKEY, O_AO_INIT) ; return (0); } 16 - 21 OLIB REFERENCE #pragma ENTER_CALL LOCAL_C INT RunIt (VOID) /* Runs the format and copy services. Designed to run in an enter harness. xf, { f_leave (p_entersend5 (w_am—->bakapp.fman,O_FMAN_FORMAT, "LOC::B:\\", "BACKUP", 0) ); p_send2 (w_am,O_AM_START) ; if (!escaped) { f_leave (p_entersend5 (w_am—>bakapp. fman, O_FMAN_COPYDEV, "LOC: :M:","LOC::B:",0)); p_send2 (w_am,O_AM_START) ; } p_send2 (w_am, O_DESTROY) ; return (FALSE) ; } LOCAL_C INT DoIt (VOID) /* Creates and initialises the application manager. Runs the format and copy services under a further enter harness. Designed to run in an enter harness to catch initialisation failures. */ { INT err; escaped=FALSE; w_am=(PR_BAKAPP *) f_new(CAT_MCOPY_MCOPY,C_BAKAPP) ; p_send2 (w_am,O_AM_INIT); err=p_enterl (RunIt); p_send2 (w_am, O_DESTROY) ; return(err); } #pragma restore GLDEF_C main(VOID) /* Copy all of M: to an SSD in B: tf { p_linklib(0); p_printf ("Backing up M: to B:"); /* convenient way to start up the console device */ return (p_enterl (DoIt)); } The nested p_enter protection for the calls to port and Runit ensure that the application manager is not sent a DEsTRoy message if it fails to be created, but is destroyed in the event of any other failure. The pEstRoy message is not strictly necessary since the operating system will clean up all resources used by an application when the application terminates. Nevertheless, it is good practice to ensure that an application is in a fit state to free its resources at any time. In this case, by use of the auto-destruction mechanism, destroying the application manager will cause the destruction of both the file manager and the preakkeEy active object. The superclass active object dest roy method ensures that any outstanding console keyboard read is cancelled and that the console device (whose handle is stored in active. pcb) is closed. 16 - 22 CHAPTER 17 THE LOCS LOCAL FILE SCAN CLASS flags pcb pname match info name wildname 1ls_matchname ls_scan ls_filename The tocs abstract class provides a set of methods to perform a synchronous scan of the LOC:: and ROM:: filing systems to locate file names which match a wildcarded name. It is similar in purpose to, but simpler than, the asynchronous scanning classes described in the File Active Objects and File Lists chapters. LOCS uses a synchronous scan, so once a scan has been started no other events can be processed until the scan completes. However, since file system requests on the LOC:: and ROM:: filing systems complete very quickly, this is unlikely to cause any significant loss of responsiveness to user input. Locs must be subclassed to provide the deferred Ls_FILENamME method, to process matching file names. Precursors The reader is assumed to understand: e the PLIB/EPOC file system directory read functions e =the LOC:: and ROM:: filing systems 17-1 OLIB REFERENCE Class definition The tocs class subclasses root and is defined in the sub-category file factive.cl (with generated header file factive.g). CLASS locs root Synchronous Local/ROM filing system file finder { ADD 1ls_matchname ADD 1ls_scan DEFER 1s_filename CONSTANTS { LOCS_FLG_ROM LOCS_FLG_LOC LOCS_FLG_ROOT Matching name checker Start the scan off Process located file name 0x1000 0x2000 (0x4000 | LOCS_FLG_LOC) LOCS_FLG_ROOT_ONLY 0x4000 Internal to locs only } PROPERTY } { UWORD flags; UBYTE *pcb; UBYTE *pname; UBYTE *match; P_INFO info; Controlling flags Open directory file handle Where to read names into Match name string Currently found file info UBYTE name [P_FNAMESIZE]; UBYTE wildname [P_FNAMESIZE]; } Property LOocs Locs Locs locs. locs locs 17-2 Ltocs. flags -pcb -pname -match info -name .-wildname controlling flags to drive the scan. This field should not be accessed by any subclass. the currently opened directory file handle. It should not be accessed by any subclass. a pointer to the name and extension within the full file specification in the locs.name buffer. A subclass may use this pointer to read the file name. a pointer to the name and extension within the wildcard full file specification in the locs.wildname buffer. Used to determine if a generated file name matches the name requirements. A subclass should treat this as read only. the file information corresponding to the file name in 1ocs.name, read from the currently opened directory file. A subclass should treat this as read only. the full file specification of the current file, whose file information is in locs.info. A subclass should treat this as read only. contains a parsed version of the wildcarded name passed to the 1s_scan method. If necessary, it is modified during the scan in order to search the ROM.:: and/or the root directories of the LOC:: filing systems. 17 THE LOCS LOCAL FILE SCAN CLASS LOCS methods JLS SCAN Perform a file scan VOID 1ls_scan(UBYTE *pname, INT flags); Perform a synchronous search for files with names matching the wildcard string pointed to by pname and of type and location specified by flags. This method will not return until the scan is complete. The value of flags may be any ored combination of items from the following two groups: P_FAMOD select only modified files P_FAHIDDEN include hidden files P_FASYSTEM include system files LOCS_FLG_ROM scan the ROM-:: device (scanned first) LOCS_FLG_LOC scan the specified directory of all LOC:: devices (in alphabetical order) LOCS_FLG_ROOT scan the root directory of a LOC:: device before scanning any specified directory The method first copies the value of flags into locs. flags and parses the string pointed to by pname into the 1locs.wildname buffer. It then reads file names, from the directory files specified by pname and flags, into the 1ocs.name buffer. For each file name it sends an Ls_mMaTCHNAME message. If this returns TRUE it also sends an LS_FILENAME message. The sending of this message is under the protection of p_enter so that the 1s_scan method itself will never be terminated by a call to p_leave. The scan is terminated either when there are no more file names to read or when the 1s_ filename method returns a non-zero value. For example: p_send4 (locs, O_LS_SCAN, "fon\\*. fon", LOCS_FLG_ROM|LOCS_FLG_ROOT | LOCS_FLG_LOC) ; will find all fon files in the ROM, and in the root and \fon directories of all SSDs in the LOC:: filing system. J LS MATCHNAME Check file name match INT 1ls_matchname (VOID) ; Check the file name pointed to by 1ocs.pname, whose file type information is in locs. info, against the wildcarded name pointed to by 1ocs.match and the file types in locs. flags. This method is not called when the name is of a volume or a directory file. Returns true if the file matches the specified requirements else FaLsE. Deferred LOCS methods LS FILENAME Process a file name INT ls_filename(UBYTE *pname) ; This method should contain the logic to process the name of a file that matches the initial specifications as passed to the 1s_scan method. It is called from the 1s_scan method each time the 1s_matchname method returns TRUE, with pname pointing to the full file specification in the 1ocs.name buffer. If the subclass is only interested in the file name and extension, it may read this via locs.pname. The method is expected to return raLsE to continue the scan or, having found a file that satisfies its requirements, it may return TRUE to terminate the scan immediately, causing the 1s_scan method to return. If this method does not return TRUE the scan will terminate when the ts_scan method has no more file names to read. 17 -3 CHAPTER 18 SYSTEM SERVICES Each member of the SIBO family of machines supplies one or more of the following global system services: e to allocate or replace an icon position, or to remove an icon e torun a file-based application by specifying the file that it is to open e¢ to nominate the current link paste server e to provide the process id of the current link paste server, if any All these services are available on MC 200/400 machines, where they are provided by the System process, SYSS$SHLL. On Series 3 and HC machines, only the two link paste related services are available and are supplied by the window server process, syss$wsRV. The available services are accessed by means of an inter-process message being sent to the supplying process. The system class provides a simplified form of access that hides the inter-process messaging mechanism. Precursors The reader is assumed to understand: e the initialisation of the appman class SYSTEM SYSTEM pid sy_init sy_icon_pos sy_link_server sy_link_paste sy_exec_open The system class provides simple access to the available system services, as described above. An application does not normally explicitly create and initialise an instance of the system class. On MC and Series 3 machines, an instance of the system class is usually created and initialised automatically by passing a flag (FLG_APPMAN_SYSTEM on the MC, or FLG_APPMAN_LINKING on the Series 3) to the application manager's aM_In1T method. In this case the handle of the created and initialised system instance is stored in the application manager's appman. system property field. Note that FLG_APPMAN_LINKING (which is defined in hwimman.g) and FLG_APPMAN_SYSTEM cause the appropriate process name (syS$SHLL or SYS$wsRvV respectively) to be used by the application manager's am_init method. 18-1 OLIB REFERENCE Class definition The system class subclasses root and is defined in the sub-category file appman.cl (with generated header file appman.g). CLASS system root For access to system services ADD sy_init Gets the pid of the supplying process ADD sy_icon_pos Allocate/remove/replace an icon position ADD sy_link_server Nominate oneself as the link paste server ADD sy_link_paste Get pid and format mask of link paste server ADD sy_exec_open Execute an application to open specified file CONSTANTS IC_SYSTEM_ALLOC 0) Allocate an icon position IC_SYSTEM_REMOVE 1 Remove an icon position IC_SYSTEM_REPLACE 2 Replace an icon position } PROPERTY UWORD pid; Process id of shell } } Property system.pid the process id of the System process. This is private to the SYSTEM class and should not be accessed by any other code SYSTEM methods J SY_INIT Initialise VOID sy_init (TEXT *sysnam) ; Initialise, by finding - and storing in system. pia - the process id of the System application whose application name is sysnam. Returns zero if successful. On error, writes nothing to system. pid and returns the relevant error number. As explained earlier, this method is not normally called explicitly by application code. It is called by the application manager in its am_init method which supplies the appropriate text. SY_ICON_POS Control icon positioning VOID sy_icon_pos(UINT req, P_POINT *pos, UINT prev); This service is only available on MC 200/400 machines. Control the positioning of icons representing tasks. The behaviour depends on the value of req, as follows: IC_SYSTEM_ALLOC Allocate an icon position, writing the allocated position in *pos and returning an index corresponding to this position. If prev is -1, the icon is allocated at the first free position. Otherwise prev should be an index returned by a previous sy_IcoN_Pos message, when an attempt will be made to allocate the corresponding position. If this position is occupied, the first free position is allocated, as for a prev of -1. IC_SYSTEM_REMOVE Remove the icon at position *pos, which should contain a position previously generated by an sy_sysTEM_Pos message with a req of either IC_SYSTEM_ALLOC Of IC_SYSTEM_REPLACE. The value of prev is ignored. Returns zero. IC_SYSTEM_REPLACE Replace the position corresponding to the index value in prev (which should be an index returned by a previous sy_1con_Pos message) with the position in *pos. The value in *pos is updated to be either the 'snap' position nearest to the passed position, or the original position if this nearest position is occupied. Returns an index corresponding to the new position. 18 -2 18 SYSTEM SERVICES SY_LINK_SERVER Set link paste server INT sy_link_server(ULONG fmask) ; Nominate the current process (that is, the process which sends this message) as the link paste server. The value of fmask is a bit mask of the formats in which the server is prepared to provide data. The formats are discussed in the description of the L1nxsv class definition in the Link Paste chapter of this manual. Returns zero. SY_LINK_PASTE Get link paste server INT sy_link_paste(ULONG *pfmt) ; Find the process id of the application (if any) that is the current link paste server. If such a process exists, writes the bit mask of available formats to *pfmt and returns the process id. The formats are discussed in the description of the L1nxsv class definition in the Link Paste chapter of this manual. If there is no current link paste server, writes nothing to *pfmt and returns zero. SY_EXEC OPEN Run an application by file name INT sy_exec_open(TEXT *pname) ; This service is only available on MC 200/400 machines. Queue a request to run the appropriate application that will open the file whose name is pointed to by pname. The application is selected on the basis of the file name extension together with the application/extension associations declared in one or more def.ext files. Returns zero without waiting for the request to complete. CHAPTER 19 INTER-PROCESS COMMUNICATION This chapter describes the 1pcs and sERVER classes that may be used to receive and process inter-process messages from one or more sources. A process running under EPOC may open only one message channel for receiving inter-process messages. Inter-process messages may, however, arrive from a variety of sources. In order to distinguish between messages from different sources, it is conventional to group them, assigning a range of message type values to each source. (The type value is stored in the type field of the &_messacz struct that forms the header of each inter-process message.) The following message groups are defined and used by existing software: CONSOL message types 0 to OxOf (MC 200/400 only) TOPLIP message types 0x10 to Ox1f (MC 200/400 only) LINKSV message types 0x20 to Ox2f (link paste) Automatic test system message types 0x30 to Ox3f It is usually convenient to use a separate server object (by definition, a subclass of SERVER) to process the receipt of inter-process messages of each group. For example, all message types concerned with link paste should be handled by a link paste server object. The rpcs class provides the central mechanism by which a process receives an inter-process message and directs it to the appropriate server object. The relationship between 1pcs and server objects is analogous to that between appman and active objects. It may be noted that a process only needs to create an instance of the rpcs class (a message channel) if it receives messages; a process may send an inter-process message without opening a message channel. Precursors The reader is assumed to understand: e the active class and the application manager's event scheduling mechanisms e the PLIB inter-process messaging services, described in the Processes and Inter-process Messaging chapter of the PLIB Reference manual e =the p_enter and p_leave error handling services Inheritance tree — fs rec : — ( active / sS ) U =. Pa es C Ilpcs / “Ss ) ‘ Pat cae ( server / ~~ {n} ) ee 19-1 OLIB REFERENCE IPCS ACTIVE gq priority isactive pcb stat destroy ao_init ao_cancel ao_queue ao_run ao_abrun ip_add_server The recs class is an active object that provides services to receive inter-process messages and dispatch them to the appropriate server object. This class should be used when several servers are required, to process messages of more than one group. It should also be used if the application is to support link paste. The application can then take advantage of the link paste server (described in the following chapter) even if no other servers are required. In other cases, where only one group of message types is to be processed, it may be more convenient to subclass recs so that the messages are processed in the subclass ao_run method, rather than in a separate server. An instance of the recs class is created and initialised automatically during the application manager's am_init method, provided the rLc_appman_1pcs flag is set, and its handle is written to appman.ipcs. If you subclass rpcs you must explicitly create and initialise the instance yourself. You must not specify the rLG_appman_ipcs flag in the am_In1IT message to aPpMaN (attempting to open more than one messaging channel will cause the application to fail). You may, however, store the handle in the application manager's appman.ipcs So that the object will be destroyed automatically when the application manager is destroyed at termination of the application. Class definition Defined in sub-category file appman.cl (generated header file appman.g). CLASS ipcs active The ipc server active object for inter-process communication { REPLACE destroy Set pcb to NULL as not really handle based REPLACE ao_init Init message queue, add to appman queue, read REPLACE ao_cancel Cancel a queued message read REPLACE ao_queue Queue a message read REPLACE ao_run Despatch message to a server REPLACE ao_abrun Queue a read and supersend ADD ip_add_server Add a server to the queue PROPERTY { P_QUE hd; Server queue header } } Property ipes.hd the head of the queue of server objects that can accept IPC messages. It should not be read or manipulated by any subclass. 19-2 19 INTER-PROCESS COMMUNICATION IPCS methods J DESTROY Destroy VOID destroy (VOID) ; Set active.pcb to NULL and supersend the pEstRoy message. Any non-zero value in active.pcb is a pointer to a message buffer for a received message; this is set in the ao_queue method. If active.pcb is non-zero, the superclass dest roy method assumes that it is an I/O channel and attempts to close it. Setting it to zero avoids the problem (and no information is "lost" by doing so). AO_INIT Initialise VOID ao_init (UINT size, UINT num); Initialise the IPCS object by performing a number of activities. e Initialise an empty server queue in ipcs.hd e Initialise a message queue of num messages; each message slot will consist of an E_MESSAGE header plus a buffer of length size. The queue is initialised using the PLIB function p_minit. Note that it is the receiver that specifies the size (and, by implication, the structure) of an inter- process message. e If the creation of the message queue is successful, the object adds itself, with priority PRIORITY_ACTIVE_1pcs, to the application manager's active object task queue and then sends itself an Ac_QUEUE Message. Calls p_leave on error. AO_QUEUE Queue a message read VOID ao_queue (VOID) ; If a message read is currently outstanding, indicated by active.isactive not set to FALSE, the method does nothing. If a message read is not outstanding, it queues a message read by calling p_mreceive, USINg active.stat as the completion status word. On completion of the read, the pointer to the received message slot will have been written to active.pcb. If the call to p_mreceive succeeds, active.isactive is set to TRUE. The call to p_mreceive will panic if messages have not been initialised or if an asynchronous message receive request is already pending. J AO CANCEL Cancel read request VOID ao_cancel (VOID); If a message read is not currently outstanding, indicated by active.isactive Set to FALSE, the method does nothing. If a message read is outstanding, it calls p_mcance1 to cancel any pending asynchronous request to receive a message and then waits (with p_waitstat ON active.stat) for the cancel to complete; it then sets active.isactive lO FALSE. AO_RUN Process a message INT ao_run(VOID); Pass the message to one of the servers in the server queue. Scans the items in the server queue until one is found that is prepared to process the type of message that has arrived (by comparing the message type with the upper and lower message type limits stored in each server's property). Calls p_panic (P_PANIC_P_IPcS_2) if no such server is found. 19-3 OLIB REFERENCE The first server that is prepared to process the message is sent an sv_RUN message under the protection of a p_enter (any error in the server's sv_run method is expected to result in a call to p_leave with a non zero error number). On detection of such an error the server is sent an sv_ABRUN message, after which the ao_run method itself calls p_leave to propagate the error (thus causing the receipt of an ac_ABRUN message). If there are no errors, the ac_run method sends an ao_QuEvE message to read another message and then returns RUN_ACTIVE_USED. AO_ABRUN Handle error VOID ao_abrun (VOID); Send an ao_QuEuE message to read another message before supersending the ao_aBRUN message. IP_ADD SERVER Add item to server queue VOID ip_add_server(PR_SERVER *hand) ; Add the server object pointed to by hand to the end of the server queue which is anchored in ipcs.had (i.e. in the 1pcs object). Unlike the application manager's queue, there is no priority system. If two servers can both handle a message of a particular type, the one that was first added to the server queue will be sent the sv_RuN message. Such a state is, in general, a programming error. Normally, each server is expected to handle a unique range of message types. Note that the message types handled by a server fall into a single range. SERVER SERVER destroy sv_abrun sv_init The server abstract class defines a set of services for processing a received inter-process message. SERVER must be subclassed to provide at least an sv_run method in order to create a useful server object. An example, the link paste server object, is described in the following chapter. Server objects work closely with an instance of the rpcs class (described above) whose handle is stored in the application manager's appman.ipcs. 19-4 19 INTER-PROCESS COMMUNICATION Class definition Defined in sub-category file ipc.cl (generated header file ipc.g). CLASS server root { REPLACE destroy Dequeue and supersend ADD sv_abrun=p_dummy Handle leave from sv_run ADD sv_init Queue to ipcs DEFER sv_run Process message PROPERTY { P_QUE q; Queue header UWORD t1; Server type range (lowest msg number) UWORD t2; (highest msg number) UWORD cid; Client process id, or zero for any process } } Property server.q used to add the server to an 1pcs server queue. It should not be accessed by any subclass. server.tl the lowest message type that is acceptable to this server. It is read by 1pcs and should not otherwise be accessed. server.t2 the highest message type that is acceptable to this server. It is read by recs and should not otherwise be accessed. server.cid intended for the storage of a client process id, if any. It is not used by either the sERvER or 1pcs Classes and is therefore free for use by any subclass. It may be used to store other information, if so required. SERVER methods J DESTROY Destroy VOID destroy (VOID); If the server object sits in an 1pcs server queue, i.e the queue anchored in ipcs.hd, remove it from the queue using p_deque. Then supersend a pEsTRoy message. SV_INIT Initialise VOID sv_init (UINT tl, UINT t2); Send an IP_ADD_SERVER message to the object whose handle is stored in the applications manager's (w_am) appman.ipcs property field. This object is assumed to be an instance of (a subclass of) the 1pcs class. Copies t1 and t2 to server.t1 and server.t2 respectively, to set the range of message types acceptable to this server. It is assumed that ¢2 is greater than (or equal) to ¢1. SV_ABRUN Handle error VOID sv_abrun (VOID); This method does nothing. It is called by recs, following a p_ieave in a server's sv_run method. A subclass may replace this method to perform specific error handling, over and above that subsequently executed in the 1Pcs ao_abrun method. This method is not expected to call p_leave. 19-5 OLIB REFERENCE Deferred SERVER methods SV_RUN Process a message VOID sv_run(E_MESSAGE *pmess) ; Process the message pointed to by pmess. This message is sent by 1pcs when it has determined that the message that has arrived is within the range of types that this server is prepared to process. 19-6 CHAPTER 20 LINK PASTE Link paste is the term used for the transfer of data from one application to another, for example to transfer a database record into the document currently being edited in a word processor. Link paste is initiated by the Bring command in a Series 3 application and by a Link command in an MC 200/400 application. To clarify terminology, this chapter refers to a receiver and a supplier. A receiver is the process which asks for the data while the supplier is the process which provides that data. In the context of link-paste, a client is synonymous with a receiver while a server is synonymous with a supplier. The following description applies to applications written for the Series 3 or the MC 200/400. It does not apply to applications on the HC, since in this case the system class does not automatically provide access to the appropriate system services (see the System Services chapter of this manual) In general, custom applications written for the HC will need to provide their own means (usually by explicit inter-process messaging) for the receiver to identify a suitable supplier. Once this is done, however, the data may be transferred using instances of LInkcL and a subclass of L1nxsv, as described below. Link paste is implemented by means of inter-process messages sent by the receiver which are handled by the supplier of the data. The supplier uses a link paste server object which is generally an instance of a subclass of L1nxsv (link server). The receiver generally uses an instance of the L1nxcu (link client) class. A supplier of data is the passive partner in the transfer in the sense that it replies to IPCS messages sent by the receiver. A supplier does not send IPCS messages to the receiver. (See the description of p_msendreceivew in the PLIB Reference Manual) The supplying process must, however, register itself if it is capable of supplying data and must be able to specify in which set of data formats it is prepared to supply the data. A Series 3 or MC 200/400 application may register itself as the current supplier by sending an sy_LINK_SERVER message to an owned instance of the system class. For example, if a word processor has a highlighted region of text when it is sent into background, it will, in general, register itself as the current supplier at that point. The range of possible formats are declared, for convenience, in the L1nxsv class definition. Their interpretation is significant to the application rather than to the LINKcL and Linxsv classes. A typical transaction is initiated by the receiver and requires the receiver to perform the following activities:- e It must first locate a process that is prepared to provide data in a suitable format. In the case of an application running on either the Series 3 or an MC machine, this is done by sending an SY_LINK_PASTE message to an owned instance of the system class to retrieve both the process id of the process which is currently registered as a supplier of data and a bit mask of the available data formats. e Jt must, at some stage, create an instance of the L1nxct class and send it an Lc_START message, passing the process id of the supplier and the particular format, from those available, in which the data is required. e It sends one or more Lc_GET_DATA messages (to the LINKCL object), passing both the address of a buffer which is ready to receive the data and the maximum length of data which can be handled. Once data has been received, it can be transferred into the application's own data structures. e =This should continue until either the available data is exhausted, in which case the transaction is automatically terminated, or until the receiver does not wish to receive further data. In this second case the receiver must send an Lc_sToP message (to the LINKCL object) to terminate the transaction. 20-1 OLIB REFERENCE The methods in the L1nxc1i and Linxsv classes and the particular values that their parameters take, in a sense, establish a protocol for communication between the receiver and supplier. Precursors The reader is assumed to understand: e inter-process messaging e the SERVER and recs classes Class diagram fe oe ¢ server / = Aes. \ Patt | | aoe —~ i linkcl / ) ee / linksv / as ) aa LINKCL destroy lc_start lc_get_data lc_stop The t1nxcu class provides the methods by which a process may initiate a link paste data transfer, receive one or more sections of data and, if necessary, terminate the transaction. Class definition Defined in sub-category file ipc.cl (generated header file ipc.g). CLASS linkcl root The link client { REPLACE destroy Stop the transaction then supersend ADD lc_start Start a transaction ADD lc_get_data Get a data record ADD lc_stop Stop the transaction PROPERTY { UWORD pid; Process ID of link server } } Property linkcl.pid the process id of the process that is currently acting as the link paste server 20 - 2 20 LINK PASTE LINKCL methods J DESTROY Destroy VOID destroy (VOID); Stops any currently outstanding transaction with the link paste server by sending an Lc_sTop message before supersending the pEsTRoy message. LC_ START Initiate a transaction VOID lc_start (INT pid, INT format); Initiate a link paste transaction, requesting data of the type specified by format from the current link paste server process. pid is the process id of the current link paste server as retrieved by sending a sy_LINK_PASTE message to the receiver's owned system object. Records the link paste server's process id by setting 1inkcl.pid to pia and then sends a Ty_LINKSV_STEP inter-process message, containing the required data format, to the current link paste server process. Calls p_leave on error. LC GET DATA Request data UINT lc_get_data(UBYTE *buf, UINT len); Request up to 1en bytes of data to be written to the buffer pointed to by but. Sends a Ty_LINKSV_STEP inter-process message, containing the buffer pointer and maximum length, to the link paste server. If there is no more data to receive, 1inkcl.pid is set to zero, automatically terminating the transaction. A further Lc_sTART message is required in order to receive the data again. Returns one of: e the (positive) length of data written by the link paste server to the buffer, e «£ FILE_Eor if there is no more data to receive, e £_GEN_FaAIL if the transaction has not been initiated. Calls p_ieave on all other errors. LC STOP Terminate a transaction VOID lc_stop (VOID) ; Terminate any transaction with a link paste server. It effectively cancels any current transaction and then sets 1inkcl.pid to zero. It is harmless if there is no current transaction. An Lc_START message is required to start a further transaction. Calls p_leave on error. 20 - 3 OLIB REFERENCE LINKSV SERVER LINKSV q buf tl len t2 cid destroy sv_init sv_abrun sv_run ae Sv is_set_format ls_get_data The t1nxsv abstract class provides the basic mechanisms for supplying, on request, sections of application data in one of a variable number of formats. LINKsv must be subclassed to supply the two deferred methods which depend on the way that the application interprets the data formats. The formats are discussed in the HWIM Reference manual. Class definition Defined in sub-category file ipc.cl (generated header file ipc.g). CLASS linksv server The link paste server { REPLACE sv_init Supersend with parameters REPLACE sv_run Process message DEFER ls_set_format Set the desired format DEFER ls_get_data Get a data record CONSTANTS { ! Message types TY_LINKSV_STEP 0x21 Link paste step TY_LINKSV_DEATH Ox22 Death of client ! Data formats - formats 0 to 31 inclusive are reserved for use by Psion DF_LINK_NATIVE 0 known only to another invocation of the supplying application DF_LINK_TEXT 1 plain ASCII text DF_LINK_TABTEXT 2 ASCII text including tab characters DF_LINK_VOICE 3 voice processor data DF_LINK_PARAS 4 text with a paragraph structure DF_LINK_SPR io) spreadsheet data DF_LINK_WRD 6 word processor data (only for Series 3a and later machines) DF_LINK_AGD q agenda data (only for Series 3a and later machines) } PROPERTY { UBYTE *buf; Set by li_get_data UWORD len; Set by li_get_data } } Property linksv.buf a pointer to data, in the required format, available for copying to the client. This is read by the sv_run method and should be set by the deferred 1s_get_data method. linksv.len the length of the data pointed to by 1inksv.buf. This is read by the sv_run method and should be set by the deferred 1s_get_data method. 20-4 20 LINK PASTE LINKSV methods SV_INIT Initialise VOID sv_init (VOID) ; Supersend the sv_tn1tT message, specifying Ty_LINKSv_sTEP and Ty_LINKSvV_DEATH as the lower and upper inter-process message types that this server is prepared to handle. SV_RUN Process a message VOID sv_run(VOID); Process a received link paste inter-process message in the range Ty_LINKSV_STEP tO TY_LINKSV_DEATH. The handling of an inter-process message is fairly complex and depends on a number of factors, not least of which is the type of inter-process message received! Inter-Process Message ty_uinxsv_sTEP An inter-process message of type Ty_LINKsv_sTEP is part of a sequence of such messages transferring data from the client to the server. The method distinguishes between the first Tty_LINKSV_sTEP inter-process message and subsequent inter-process messages of this type. First ry_tinxsv_step inter-process message The first ty_LINKSV_STEP inter-process message represents a new transaction and corresponds to the client sending an Lc_sTarT message to an instance of its LtnKcL object. The link-server object decides that this is the first time if the property server.cid is zero. The following is done:- e The process ID of the client is saved in server.cid. @ p_logon is called to request that the syssmane process send it a TY_LINKSV_DEATH inter- process message if the client terminates. e The Ty_LINksv_sTEP inter-process message contains the required data format and this is passed as the parameter to an Ls_sET_FORMAT message which should register the format in which the data is to be provided. e The inter-process message is freed by calling p_mfree with a reply value of zero. Subsequent ry_tinxsv_step inter-process messages Subsequent Ty_LINKSvV_STEP inter-process messages are expected to contain a pointer to a buffer and a maximum length in the message data, as set by the LINKcL 1c_get_data method. Two situations must be handled - the buffer pointer is zero or non-zero. Zero buffer pointer A zero buffer pointer is taken to mean that the client wishes to terminate the link paste transaction. This is effectively a cancel and corresponds to the client sending an Lc_stTop message to an instance of its LInKcL object. The following is done:- e an LS_GET_DATA message is sent with a parameter of -1. The effect of this is to cause the deferred 1s_get_data method to tidy and reset the link server object in preparation for a new transaction. ® p_logoff is called to cancel the request that the syssmanc process send a Ty_LINKSV_DEATH inter-process message on termination of the client. @ server.cid which contains the process id of the link paste client (i.e. the receiver), is set to zero, therefore losing all knowledge of that client e The inter-process message is freed (p_mfree) returning =_FILE_EoF to the client. 20-5 OLIB REFERENCE Non-zero buffer pointer A non-zero buffer is taken to mean that the client is making a request for (more) data. An LS_GET_DATA message is sent to retrieve an amount of data not exceeding the maximum length. If the 1s_get_data method returns a non-zero value:- e itis assumed that 1inksv.buf and linksv.1len have been set to indicate available data, which is copied to the client's buffer. e The inter-process message is freed (p_mfree) returning a value equal to the length of the data copied to the client's buffer. If the 1s_get_data method returns a zero value:- e this is taken to mean that no more data is available. @ p_logoff is called to cancel the request that the syssmanc process send a Ty_LINKSV_DEATH inter-process message on termination of the client. @ server.cia which contains the process id of the link paste client is set to zero, therefore losing all knowledge of that client e The inter-process message is freed (p_mfree) returning =E_FILE_EoF to the client. Inter-Process Message ry_utinxsv_pDEATH An inter-process message of type Ty_LINKSV_DEATH means that the link paste client died or was terminated during the inter-process data transfer. On receipt of this inter-process message, the following is done:- e The inter-process message is freed (p_mfree) e@ server.cid which contains the process id of the link paste client is set to zero, therefore losing all knowledge of that client e an LS_GET_DATA message is sent with a parameter of -1. The effect of this is to cause the deferred 1s_get_data method to tidy and reset the link server object in preparation for a new transaction. Deferred LINKSV methods LS SET FORMAT Set data format VOID 1ls_set_format (UINT format) ; Register the data format in which data is to be provided. For link paste initiated by one of the built-in applications, the format will be one of the pr_L1nx_xxx values listed in the L1nxsv class definition. The method should call p_1eave on error. LS GET DATA Provide data INT ls_get_data(UINT len); Provide not more than 1en bytes of data in the currently specified format. A pointer to the data and its length should be written to Linksv.buf and 1inksv.1len respectively. The method should return zero if there is no more data available, else a positive number. A value of -1 for 1en indicates that the client process no longer requires data. In this case the method should tidy up and reset any variables so that future Ls_GzT_paTa messages read the data from the start of available data. The method should call p_1eave on error. 20 - 6 INDEX .trm files serial port parameters, 8-11 ACTIVE class AO_ABRUN method, 11-4 AO_CANCEL method, 11-3 AO_INIT method, 11-3 AO_QUEUE method, 11-3 AO_RUN method, 11-4 DESTROY method, 11-3 methods, 11-3 oop, 11-1 active objects asynchronous I/O devices, 11-1 class, 11-1 file, 14-1 file server and, 11-1 priorities, 10-2 return value, 10-3 scheduling mechanism, 10-6 scheduling, 10-3 TIMER, 13-1 AIDLE class AO_INIT method, 12-2 AO_RUN method, 12-2 compute intensive tasks, 12-1 example, 12-3 idle time computation, 12-3 methods, 12-2 oop, 12-1 pause an operation, 12-3 AIDLE example oop, 12-3 AM_ADD_TASK APPMAN class method, 10-9 AM_CHANGE_PRI APPMAN class method, 10-12 AM_CLEAN_UP APPMAN class method, 10-11 AM_FINDIMG AM_INIT APPMAN class method, 10-5 AM_LOAD_RES_BUF APPMAN class method, 10-10 AM_LOAD_RESOURCE APPMAN class method, 10-9 AM_NOTIFY APPMAN class method, 10-10 AM_NOTIFYERR APPMAN class method, 10-11 AM_ONLYONE APPMAN class method, 10-12 AM_RSCNAME APPMAN class method, 10-10 AM_START APPMAN class method, 10-6 AM_STOP APPMAN class method, 10-9 AM_WAIT APPMAN class method, 10-6 ANIMATOR class AO_INIT method, 13-4 AO_RUN method, 13-4 methods, 13-4 oop, 13-3 AO_ABRUN ACTIVE class method, 11-4 FACTIVE class method, 14-3 FMFMT class method, 16-14 FMSCAN class method, 16-15 IPCS class method, 19-4 PNODE class method, 15-4 PSEL class method, 15-7 AO_CANCEL ACTIVE class method, 11-3 BUZSND class method, 13-6 FACTIVE class method, 14-2 FCASY class method, 14-12 IPCS class method, 19-3 PSEL class method, 15-7 AO_INIT ACTIVE class method, 11-3 AIDLE class method, 12-2 ANIMATOR class method, 13-4 BUZSND class method, 13-5 FACTIVE class method, 14-2 IPCS class method, 19-3 PSEL class method, 15-7 TIMER class method, 13-2 AO_QUEUE ACTIVE class method, 11-3 BUZSND class method, 13-6 FCASY class method, 14-12 FCSYNC class method, 14-15 FNODE class method, 14-9 FSCAN class method, 14-5 IPCS class method, 19-3 TIMER class method, 13-2 AO_RUN ACTIVE class method, 11-4 AIDLE class method, 12-2 ANIMATOR class method, 13-4 BUZSND class method, 13-6 FCASY class method, 14-13 FMFMT class method, 16-13 FMMK class method, 16-12 FNODE class method, 14-9 FSCAN class method, 14-5 IPCS class method, 19-3 APPMAN class AM_ADD_TASK method, 10-9 AM_CHANGE__ PRI method, 10-12 AM_CLEAN_UP method, 10-11 AM_FINDIMG method, 10-11 OLIB REFERENCE AM_INIT method, 10-5 cl_remove AM_LOAD_RES_BUF method, 10-10 CLEANUP class function, 9-6 AM_LOAD_RESOURCE method, 10-9 CL_REMOVE AM_NOTIFY method, 10-10 AM_NOTIFYERR method, 10-11 AM_ONLYONE method, 10-12 AM_RSCNAME method, 10-10 AM_START method, 10-6 AM_STOP method, 10-9 AM_WAIT, 10-6 definition, 10-4 diagram, 10-4 methods, 10-5 CLEANUP class method, 9-3 CL_SET_LEVEL CLEANUP class method, 9-4 class ACTIVE, 11-1 AIDLE, 12-1 ANIMATOR, 13-3 application manager definition, 10-4 application manager diagram, 10-4 application manager, 10-1 oop, 10-1 application manager property, 10-5 property, 10-5 BFILE, 8-2 ARRAY VARIABLE binary files, 8-1 classes, 5-1 BUZSND, 13-4 asynchronous cleanup, 9-1 I/O devices active object, 11-1 editable documents, 6-1 BFILE class EPFLAT, 6-7 DESTROY method, 8-3 EPROOT, 6-2 FI_CLOSE method, 8-3 EPSEG, 6-10 FI_OPEN method, 8-3 FACTIVE, 14-1 FI_READ method, 8-3 FCASY, 14-11 FL_REWIND method, 8-4 FCSYNC, 14-14 FL_SENSE_DATA method, 8-4 FMAN, 16-2 FL_SET_BUF_LEN method, 8-3 FMFMT, 16-13 methods, 8-3 FMMK, 16-12 oop class, 8-2 FMSCAN, 16-14 BINARY FILE FMSRC, 16-17 classes, 8-1 FMTARG, 16-18 bring FNODE, 14-8 IPCS, 20-5 FSCAN, 14-3 oop, 20-1 idle object, 12-1 BUZSND class IPCS, 19-2 AO_CANCEL method, 13-6 LINKCL, 20-2 AO_INIT method, 13-5 LINKSV, 20-4 AO_QUEUE method, 13-6 PNODE, 15-3 AO_RUN method, 13-6 PSEL, 15-4 methods, 13-5 PSELVAR, 15-2 oop, 13-4 resource files, 7-1 cl_add ROOT, 2-1 CLEANUP class function, 9-4 SCAN, 17-1 CL_ADD SERFILE, 8-11 CLEANUP class method, 9-3 SERVER, 19-4 cl_add_alloc SGBUF, 4-1 CLEANUP class function, 9-5 SYSTEM, 18-1 cl_add_dyl TIME, 3-1 CLEANUP class function, 9-5 TIMER, 13-1, 13-2 cl_add_iochan TLVDATA, 8-8 CLEANUP class function, 9-5 TLVFILE, 8-4 cl_add_object VAFIX, 5-9 CLEANUP class function, 9-4 VAFLAT, 5-14 cl_add_shared variable array classes, 5-1 CLEANUP class function, 9-5 VAROOT, 5-3 cl_clean_item VASEG, 5-16 CLEANUP class function, 9-6 VASTR, 5-11 CL_CLEAN_ITEM VAXVAR, 5-18 CLEANUP class method, 9-3 VAXVARS, 5-21 CL_CLEAN_LEVEL class diagrams CLEANUP class method, 9-4 OLIB hierarchy, 1-3 CL_INIT OLIB library, 1-3 CLEANUP class method, 9-3 classes OLIB library overview, 1-1 OLIB library using, 1-2 CLEANUP class cl_add function, 9-4 CL_ADD method, 9-3 cl_add_alloc function, 9-5 cl_add_dyl function, 9-5 cl_add_iochan function, 9-5 cl_add_object function, 9-4 cl_add_shared function, 9-5 cl_clean_item function, 9-6 CL_CLEAN_ITEM method, 9-3 CL_CLEAN_LEVEL method, 9-4 CL_INIT method, 9-3 cl_remove function, 9-6 CL_REMOVE method, 9-3 CL_SET_LEVEL method, 9-4 DESTROY method, 9-3 functions convenience, 9-4 methods, 9-3 oop, 9-1 compute intensive tasks AIDLE class, 12-1 DatCommandPtr magic static, 10-11 DESTROY ACTIVE class method, 11-3 BFILE class method, 8-3 CLEANUP class method, 9-3 EPFLAT class method, 6-8 IPCS class method, 19-3 LINKCL class method, 20-3 ROOT class method, 2-2 RSCFILE class method, 7-2 SERVER class method, 19-5 SGBUF class method, 4-2 VAROOT class method, 5-4 documents editable classes, 6-1 DYL EP_COPY_TO_FRONT EPROOT class method, 6-5 EP_DELETE EPFLAT class method, 6-9 EPSEG class method, 6-11 EP_EXTRACT EPFLAT class method, 6-9 EPSEG class method, 6-11 EP_INIT EPFLAT class method, 6-8 EPSEG class method, 6-10 EP_INSERT EPFLAT class method, 6-8 EPSEG class method, 6-11 EP_MOD_CHARS EPROOT class method, 6-6 EP_PARA_COUNT EPROOT class method, 6-4 EP_PASTE EPROOT class method, 6-6 EP_SCAN_BLOCK EPROOT class method, 6-4 EP_SCAN_PARA EPROOT class method, 6-4 EP_SCAN_WORD EPROOT class method, 6-4 EP_SENSE_CHARS EPFLAT class method, 6-8 EPSEG class method, 6-11 EP_SENSE_LEN EPFLAT class method, 6-8 EPSEG class method, 6-11 EP_SENSE_TEXT EPROOT class method, 6-6 EP_SET_TEXT EPROOT class method, 6-3 EP_WORD COUNT EPROOT class method, 6-4 EPFLAT class OLIB library introduction, 1-1 editable documents class oop, 6-1 EF_GRANULARITY EPFLAT class method, 6-9 EF_SENSE_BUF EPFLAT class method, 6-9 EP_ADD_PARA EPROOT class method, 6-5 EP_BACK_ CHARS EPFLAT class method, 6-8 EPSEG class method, 6-11 EP_CAPACITY EPFLAT class method, 6-9 EPROOT class method, 6-6 EP_CLEAR EPFLAT class method, 6-9 EPSEG class method, 6-11 EP_COMPRESS EPFLAT class method, 6-9 EPSEG class method, 6-11 EP_COPY_INDENT EPROOT class method, 6-5 EP_COPY_TO_BACK EPROOT class method, 6-5 DESTROY method, 6-8 EF_GRANULARITY method, 6-9 EF_SENSE_BUF method, 6-9 EP_BACK_CHARS method, 6-8 EP_CAPACITY method, 6-9 EP_CLEAR method, 6-9 EP_COMPRESS method, 6-9 EP_DELETE method, 6-9 EP_EXTRACT method, 6-9 EP_INIT method, 6-8 EP_INSERT method, 6-8 EP_SENSE_CHARS method, 6-8 EP_SENSE_LEN method, 6-8 methods, 6-8 oop class, 6-7 EPROOT class EP_ADD_PARA method, 6-5 EP_CAPACITY method, 6-6 EP_COPY_INDENT method, 6-5 EP_COPY_TO_BACK method, 6-5 EP_COPY_TO_FRONT method, 6-5 EP_MOD_CHARS method, 6-6 EP_PARA_COUNT method, 6-4 EP_PASTE method, 6-6 EP_SCAN_BLOCK method, 6-4 INDEX iii OLIB REFERENCE EP_SCAN_PARA method, 6-4 EP_SCAN_WORD method, 6-4 EP_SENSE_TEXT method, 6-6 EP_SET_TEXT method, 6-3 EP_WORD COUNT method, 6-4 methods deferred, 6-6 methods, 6-3 oop class, 6-2 EPSEG class EP_BACK_CHARS method, 6-11 EP_CLEAR method, 6-11 EP_COMPRESS method, 6-11 EP_DELETE method, 6-11 EP_EXTRACT method, 6-11 EP_INIT method, 6-10 EP_INSERT method, 6-11 EP_SENSE_CHARS method, 6-11 EP_SENSE_LEN method, 6-11 methods, 6-10 oop class, 6-10 error handling OLIB library, 1-4 OLIB library panics, 1-5 FA_CLOSE FACTIVE class method, 14-3 FCASY class method, 14-13 FNODE class method, 14-10 FSCAN class method, 14-6 FACTIVE class AO_ABRUN method, 14-3 AO_CANCEL method, 14-2 AO_INIT method, 14-2 FA_CLOSE method, 14-3 methods, 14-2 oop, 14-1 FC_OPEN FCASY class method, 14-13 FC_REQUEST_COMP deferred method, 14-14 FCASY class method deferred, 14-14 FMSRC class method, 16-17 FMTARG class method, 16-18 FC_WRITE FCASY class method, 14-12 FCSYNC class method, 14-15 FCASY class AO_CANCEL method, 14-12 AO_QUEUE method, 14-12 AO_RUN method, 14-13 FA_CLOSE method, 14-13 FC_OPEN method, 14-13 FC_WRITE method, 14-12 methods deferred, 14-14 methods, 14-12 oop, 14-11 FCASY sub-class oop, 14-1 FCSYNC class AO_QUEUE method, 14-15 FC_WRITE method, 14-15 methods, 14-15 oop, 14-14 FCSYNC sub-class oop, 14-1 FI_CLOSE BFILE class method, 8-3 FI_OPEN BFILE class method, 8-3 TLVFILE class method, 8-6 FI_READ BFILE class method, 8-3 file active object, 14-1 FILE BINARY classes, 8-1 file I/O active objects and, 11-1 file lists oop, 15-1 file management oop, 16-1 file manager example code, 16-19 file scan local oop, 17-1 file server active objects and, 11-1 files type-length-value, 8-4 filing system nodes, 14-8 FL_COUNT TLVFILE class method, 8-6 FL_DELREC TLVFILE class method, 8-7 FL_READ_BY_TYPE TLVFILE class method, 8-7 FL_REPLACE TLVFILE class method, 8-7 FL_REWIND BFILE class method, 8-4 TLVFILE class method, 8-6 FL_SENSE_DATA BFILE class method, 8-4 FL_SENSE_REC TLVFILE class method, 8-7 FL_SET_BUF_LEN BFILE class method, 8-3 FL_SET_REC TLVFILE class method, 8-6 FL_WRITE_REC TLVFILE class method, 8-6 FMAN class file management, 16-1 FMAN_ATTRIB method, 16-8 FMAN_CANCEL method, 16-4 FMAN_COPY method, 16-5 FMAN_COPYDEV method, 16-7 FMAN_DELETE method, 16-5 16-11 FMAN_FORMAT method, 16-8 FMAN_INFO method, 16-8 FMAN_INIT method, 16-4 FMAN_MAKE method, 16-7 FMAN_NAME method, 16-8 FMAN_COMPLETE deferred method, 16-9 FMAN_ERROR deferred method, 16-11 FMAN_FILEEXIST deferred method, FMAN_NEWNAME deferred method, 16-10 FMAN_REMOVE method, 16-7 FMAN_RENAME method, 16-6 FMAN_UPDATE deferred method, 16-11 methods deferred, 16-9 methods, 16-4 oop, 16-2 FMAN_ATTRIB FMAN class method, 16-8 FMAN_CANCEL FMAN class method, 16-4 FMAN_COMPLETE FMAN class method deferred, 16-9 FMAN_COPY FMAN class method, 16-5 FMAN_COPYDEV FMAN class method, 16-7 FMAN_DELETE FMAN class method, 16-5 FMAN_ERROR FMAN class method deferred, 16-11 FMAN_FILEEXIST FMAN class method deferred, 16-11 FMAN_FORMAT FMAN class method, 16-8 FMAN_INFO FMAN class method, 16-8 FMAN_INIT FMAN class method, 16-4 FMAN_MAKE FMAN class method, 16-7 FMAN_NAME FMAN class method, 16-8 FMAN_NEWNAME FMAN class method deferred, 16-10 FMAN_REMOVE FMAN class method, 16-7 FMAN_RENAME FMAN class method, 16-6 FMAN_UPDATE FMAN class method deferred, 16-11 FMFMT class AO_ABRUN method, 16-14 AO_RUN method, 16-13 methods, 16-13 oop, 16-13 FMFMT sub-class oop, 16-1 FMMK class AO_RUN method, 16-12 methods, 16-12 oop, 16-12 FMMkK sub-class oop, 16-1 FMSCAN class AO_ABRUN method, 16-15 FS_DIRNAME method, 16-16 FS_END_DIRLIST method, 16-16 FS_FILENAME method, 16-15 FS_FSCAN_END method, 16-16 methods, 16-15 oop, 16-14 INDEX FMSCAN sub-class oop, 16-1 FMSRC class FC_REQUEST_COMP method, 16-17 methods, 16-17 oop, 16-17 FMSRC sub-class oop, 16-1 FMTARG class FC_REQUEST_COMP method, 16-18 methods, 16-18 oop, 16-18 FMTARG sub-class oop, 16-1 FN_END_LIST FNODE class method deferred, 14-10 PNODE class method, 15-4 FN_LIST FNODE class method, 14-10 FN_NODENAME FNODE class method deferred, 14-10 PNODE class method, 15-4 FNODE class AO_QUEUE method, 14-9 AO_RUN method, 14-9 FA_CLOSE method, 14-10 FN_END_LIST deferred method, 14-10 FN_LIST method, 14-10 FN_NODENAME deferred method, 14-10 methods deferred, 14-10 methods, 14-9 oop, 14-8 FENODE sub-class oop, 14-1 FS_DIRNAME FMSCAN class method, 16-16 FSCAN class method deferred, 14-7 PSEL class method, 15-8 FS_END_DIRLIST FMSCAN class method, 16-16 FSCAN class method deferred, 14-8 FS_FILENAME FMSCAN class method, 16-15 FSCAN class method deferred, 14-7 PSEL class method, 15-8 FS_FSCAN FSCAN class method, 14-6 FS_FSCAN_END FMSCAN class method, 16-16 FSCAN class method deferred, 14-7 PSEL class method, 15-8 FS_MATCHNAME FSCAN class method, 14-6 FSCAN class AO_QUEUE method, 14-5 AO_RUN method, 14-5 FA_CLOSE method, 14-6 FS_DIRNAME deferred method, 14-7 FS_END_DIRLIST deferred method, 14-8 FS_FILENAME deferred method, 14-7 FS_FSCAN method, 14-6 FS_FSCAN_END method, 14-7 FS_MATCHNAME method, 14-6 methods deferred, 14-7 OLIB REFERENCE methods, 14-5 oop, 14-3 FSCAN sub-class oop, 14-1 functions CLEANUP class convenience, 9-4 HWIM active object, 11-1 application manager class, 10-1 idle object, 12-1 I/O asynchronous active object, 11-1 idle object class, 12-1 IDLE OBJECT class, 12-1 idle time AIDLE class computation, 12-3 INTER-PROCESS COMMUNICATIONS class, 19-1 IP_ADD_SERVER IPCS class method, 19-4 IPCS bring, 20-5 class, 19-1 link paste, 20-5 IPCS class AO_ABRUN method, 19-4 AO_CANCEL method, 19-3 AO_INIT method, 19-3 AO_QUEUE method, 19-3 AO_RUN method, 19-3 DESTROY method, 19-3 IP_ADD_SERVER method, 19-4 methods, 19-3 services, 19-2 LC_GET_DATA LINKCL class method, 20-3 LC_START LINKCL class method, 20-3 LC_STOP LINKCL class method, 20-3 library OLIB DYL introduction, 1-1 link paste IPCS, 20-5 LINK PASTE classes, 20-1 LINKCL class DESTROY class method, 20-3 LC_GET_DATA class method, 20-3 LC_START class method, 20-3 LC_STOP class method, 20-3 methods, 20-3 services, 20-2 LINKSYV class LS_GET_DATA class method, 20-6 LS_SET_FORMAT class method, 20-6 methods deferred, 20-6 methods, 20-5 services, 20-4 SV_INIT class method, 20-5 SV_RUN class method, 20-5 LOC filing system node, 14-8, 17-1 LOCS class LS_FILENAME deferred method, 17-3 LS_MATCHNAME method, 17-3 LS_SCAN method, 17-3 methods deferred, 17-3 methods, 17-3 LS_FILENAME LOCS class method deferred, 17-3 LS_GET_DATA LINKSV class method deferred, 20-6 LS_MATCHNAME LOCS class method, 17-3 LS_SCAN LOCS class method, 17-3 LS_SET_FORMAT LINKSV class method deferred, 20-6 magic static DatCommandPtr, 10-11 manager application class, 10-1 method function OLIB long parameters, 1-3 OLIB prototypes, 1-2 methods ACTIVE class, 11-3 AIDLE class, 12-2 ANIMATOR class, 13-4 APPMAN class, 10-5 BFILE class, 8-3 BUZSND class, 13-5 CLEANUP class, 9-3 EPFLAT class, 6-8 EPROOT class deferred, 6-6 EPROOT class, 6-3 EPSEG class, 6-10 FACTIVE class, 14-2 FCASY class deferred, 14-14 FCASY class, 14-12 FCSYNC class, 14-15 FMAN class deferred, 16-9 FMAN class, 16-4 FMFMT class, 16-13 FMMkK class, 16-12 FMSCAN class, 16-15 FMSRC class, 16-17 FMTARG class, 16-18 FNODE class deferred, 14-10 FNODE class, 14-9 FSCAN class deferred, 14-7 FSCAN class, 14-5 IPCS class, 19-3 LINKCL class, 20-3 LINKSV class deferred, 20-6 LINKSV class, 20-5 LOCS class deferred, 17-3 LOCS class, 17-3 PNODE class, 15-4 PSEL class deferred, 15-11 PSEL class, 15-7 PSELVAR class, 15-3 ROOT class, 2-2 RSCFILE class, 7-2 SERFILE class, 8-13 SERVER class deferred, 19-6 SERVER class, 19-5 SGBUF class, 4-2 SYSTEM class, 18-2 TIME class, 3-3 TIMER class, 13-2 TLVDATA class deferred, 8-10 TLVDATA class, 8-9 TLVFILE class, 8-6 VAFIX class, 5-10 VAFLAT class, 5-15 VAROOT class deferred, 5-7 VAROOT class, 5-4 VASEG class, 5-17 VASTER class, 5-12 VAXVAR class, 5-20 VAXVARS class, 5-22 node filing systems, 14-8 OLIB class diagrams, 1-3 class hierarchy, 1-3 classes overview, 1-1 classes using, 1-2 error handling, 1-4 error numbers panics, 1-5 library introduction, 1-1 method function long parameters, 1-3 method function prototypes, 1-2 PLIB basis, 1-1 ACTIVE class HWIM, 11-1 ACTIVE class methods, 11-3 active object priorities, 10-2 active object return value, 10-3 active object scheduling, 10-3 active object scheduling mechanism, 10-6 AIDLE class example, 12-3 AIDLE class HWIM, 12-1 AIDLE class idle time computation, 12-3 AIDLE class methods, 12-2 AIDLE class pause operation, 12-3 ANIMATOR class methods, 13-4 application manager class definition, 10-4 application manager class diagram, 10-4 application manager class HWIM, 10-1 application manager class property, 10-5 APPMAN class methods, 10-5 BFILE class, 8-2 BFILE class methods, 8-3 bring, 20-1 BUZSND class methods, 13-5 CLEANUP class functions convenience, 9-4 CLEANUP class methods, 9-3 EPFLAT class, 6-7 EPFLAT class methods, 6-8 EPROOT class, 6-2 EPROOT class deferred methods, 6-6 EPROOT class methods, 6-3 EPSEG class, 6-10 EPSEG class methods, 6-10 FACTIVE class, 14-1 FACTIVE class methods, 14-2 INDEX FCASY class, 14-11 FCASY class deferred methods, 14-14 FCASY class methods, 14-12 FCASY sub-class, 14-1 FCSYNC class, 14-14 FCSYNC class methods, 14-15 FCSYNC sub-class, 14-1 file active object, 14-1 file lists, 15-1 file management, 16-1 file manager example code, 16-19 file scan local, 17-1 FMAN class, 16-2 FMAN class deferred methods, 16-9 FMAN class methods, 16-4 FMFMT class, 16-13 FMFMT class methods, 16-13 FMFMT sub-class, 16-1 FMMkK class, 16-12 FMMkK class methods, 16-12 FMMkK sub-class, 16-1 FMSCAN class, 16-14 FMSCAN class methods, 16-15 FMSCAN sub-class, 16-1 FMSRC class, 16-17 FMSRC class methods, 16-17 FMSRC sub-class, 16-1 FMTARG class, 16-18 FMTARG class methods, 16-18 FMTARG sub-class, 16-1 FNODE class, 14-8 FNODE class deferred methods, 14-10 FNODE class methods, 14-9 FNODE sub-class, 14-1 FSCAN class, 14-3 FSCAN class deferred methods, 14-7 FSCAN class methods, 14-5 FSCAN sub-class, 14-1 inter-process communications, 19-1 IPCS, 19-1 IPCS bring, 20-5 IPCS class, 19-2 IPCS class methods, 19-3 IPCS link paste, 20-5 link paste, 20-1 LINKCL class, 20-2 LINKCL class methods, 20-3 LINKSV class, 20-4 LINKSYV class deferred methods, 20-6 LINKSV class methods, 20-5 LOCS class deferred methods, 17-3 LOCS class methods, 17-3 PNODE class, 15-3 PNODE class methods, 15-4 PSEL class, 15-4 PSEL class deferred methods, 15-11 PSEL class methods, 15-7 PSELVAR class, 15-2 PSELVAR class methods, 15-3 ROOT class, 2-1 ROOT class DESTROY method, 2-2 ROOT class methods, 2-2 RSCFILE class methods, 7-2 SCAN class, 17-1 OLIB REFERENCE SERFILE class, 8-11 SERFILE class methods, 8-13 SERVER class, 19-4 SERVER class deferred methods, 19-6 SERVER class methods, 19-5 SGBUF class, 4-1 SGBUF class methods, 4-2 SYSTEM class, 18-1 SYSTEM class methods, 18-2 system services, 18-1 TIME class, 3-1 TIME class methods, 3-3 TIMER active object, 13-1 TIMER class, 13-1 TIMER class methods, 13-2 TLVDATA class, 8-8 TLVDATA class defered methods, 8-10 TLVDATA class methods, 8-9 TLVFILE class, 8-4 TLVFILE class methods, 8-6 type-length-value files, 8-4 VAFIX class, 5-9 VAFIX class methods, 5-10 VAFLAT class, 5-14 VAFLAT class methods, 5-15 VAROOT class, 5-3 VAROOT class deferred methods, 5-7 VAROOT class methods, 5-4 VASEG class, 5-16 VASEG class methods, 5-17 VASTR class, 5-11 VASTR class methods, 5-12 VAXVAR class, 5-18 VAXVAR class methods, 5-20 VAXVARS class, 5-21 VAXVARS class methods, 5-22 panics OLIB error numbers, 1-5 pause operation AIDLE example, 12-3 PLIB OLIB relationship to, 1-1 PNODE class AO_ABRUN method, 15-4 FN_END_LIST method, 15-4 FN_NODENAME method, 15-4 methods, 15-4 oop, 15-3 PS_ASCEND_PATH PSEL class method, 15-9 PS_DESCEND_PATH PSEL class method, 15-9 PS_DRIVES PSEL class method, 15-10 PS_GET_FILE PSEL class method, 15-8 PS_GETTAG PSEL class method, 15-10 PS_NEW_LIST PSEL class method deferred, 15-11 PS_ORDER PSEL class method, 15-10 PS_SELECT_DIRENTRY PSEL class method, 15-9 viii PS_SENSE_FILENAME PSEL class method, 15-9 PS_SET_PATH PSEL class method, 15-9 PS_SETTAG PSEL class method, 15-10 PSEL class AO_ABRUN method, 15-7 AO_CANCEL method, 15-7 AO_INIT method, 15-7 FS_DIRNAME method, 15-8 FS_FILENAME method, 15-8 FS_FSCAN_END method, 15-8 methods deferred, 15-11 methods, 15-7 oop, 15-4 PS_ASCEND_PATH method, 15-9 PS_DESCEND_PATH method, 15-9 PS_DRIVES method, 15-10 PS_GET_FILE method, 15-8 PS_GETTAG method, 15-10 PS_NEW_LIST deferred method, 15-11 PS_ORDER method, 15-10 PS_SELECT_DIRENTRY method, 15-9 PS_SENSE_FILENAME method, 15-9 PS_SET_PATH method, 15-9 PS_SETTAG method, 15-10 pselvar file lists, 15-1 PSELVAR class methods, 15-3 oop, 15-2 VA_TEST method, 15-3 REM filing system node, 14-8 RESOURCE FILES class, 7-1 ROM filing system node, 14-8, 17-1 ROOT class DESTROY method, 2-2 methods, 2-2 oop superclass, 2-1 RS_INIT RSCFILE class method, 7-2 RS_READ RSCFILE class method, 7-2 RS_READ_BUF RSCFILE class method, 7-2 RSCFILE class DESTROY method, 7-2 methods, 7-2 RS_INIT method, 7-2 RS_READ method, 7-2 RS_READ_BUF method, 7-2 SB_ALLOCSEG SGBUFEF class method, 4-4 SB_BACKPOINT SGBUF class method, 4-4 SB_COMPRESS SGBUF class method, 4-4 SB_COUNT SGBUF class method, 4-4 SB_DELETE SGBUF class method, 4-3 SB_EXTRACT SGBUF class method, 4-4 SB_INIT SGBUF class method, 4-2 SB_INSERT SGBUF class method, 4-3 SB_POINT SGBUF class method, 4-3 SCAN class file scan local, 17-1 oop, 17-1 SERFILE class methods, 8-13 oop class, 8-11 TD_RESET method, 8-13 TD_SENSE_ITEM method, 8-14 TD_SET_FILE method, 8-13 TD_SET_ITEM method, 8-13 serial port parameter .trm file, 8-11 SERVER class DESTROY method, 19-5 methods deferredoop, 19-6 methods, 19-5 services, 19-4 SV_ABRUN method, 19-5 SV_INIT method, 19-5 SV_RUN deferred method, 19-6 SGBUF class DESTROY method, 4-2 methods, 4-2 oop class, 4-1 SB_ALLOCSEG method, 4-4 SB_BACKPOINT method, 4-4 SB_COMPRESS method, 4-4 SB_COUNT method, 4-4 SB_DELETE method, 4-3 SB_EXTRACT method, 4-4 SB_INIT method, 4-2 SB_INSERT method, 4-3 SB_POINT method, 4-3 sub-class FCASY, 14-1 FCSYNC, 14-1 FMFMT, 16-1 FMMK, 16-1 FMSCAN, 16-1 FMSRC, 16-1 FMTARG, 16-1 FNODE, 14-1 FSCAN, 14-1 SV_ABRUN SERVER class method, 19-5 SV_INIT LINKSV class method, 20-5 SERVER class method, 19-5 SV_RUN LINKSV class method, 20-5 SERVER class method deferred, 19-6 SY_EXEC_OPEN SYSTEM class method, 18-3 INDEX SY_ICON_POS SYSTEM class method, 18-2 SY_INIT SYSTEM class method, 18-2 SY_LINK_PASTE SYSTEM class method, 18-3 SY_LINK_SERVER SYSTEM class method, 18-3 SYSTEM class methods, 18-2 services, 18-1 SY_EXEC_OPEN method, 18-3 SY_ICON_POS method, 18-2 SY_INIT method, 18-2 SY_LINK_PASTE method, 18-3 SY_LINK_SERVER method, 18-3 SYSTEM SERVICES class, 18-1 TD_CHANGED TLVDATA class method, 8-9 TD_LOAD_ITEM TLVDATA class method, 8-10 TD_OPEN TLVDATA class method, 8-9 TD_RESET SERFILE class method, 8-13 TLVDATA class method, 8-10 TD_SAVE TLVDATA class method, 8-9 TD_SAVE_ITEM TLVDATA class method, 8-10 TD_SENSE_ITEM SERFILE class method, 8-14 TLVDATA class method deferred, 8-10 TD_SET_FILE SERFILE class method, 8-13 TLVDATA class method deferred, 8-10 TD_SET_ITEM SERFILE class method, 8-13 TLVDATA class method deferred, 8-10 TIME class methods, 3-3 oop class, 3-1 TO_ADD_DAYS method, 3-5 TO_ADD_MONTHS method, 3-5 TO_ADD_SECS method, 3-4 TO_ADD_YEARS method, 3-5 TO_GET_SYSDAT method, 3-7 TO_SENSE method, 3-4 TO_SENSE_FORMAT method, 3-6 TO_SET method, 3-3 TO_SET_FORMAT method, 3-6 TIMER class active object, 13-1 AO_INIT method, 13-2 AO_QUEUE method, 13-2 methods, 13-2 oop, 13-1, 13-2 TM_QABSOLUTE method, 13-3 TLVDATA class methods deferred, 8-10 methods, 8-9 oop class, 8-8 TD_CHANGED method, 8-9 OLIB REFERENCE TD_LOAD_ITEM method, 8-10 TD_OPEN method, 8-9 TD_RESET method, 8-10 TD_SAVE method, 8-9 TD_SAVE_ITEM method, 8-10 TD_SENSE_ITEM method deferred, 8-10 TD_SET_FILE method deferred, 8-10 TD_SET_ITEM method deferred, 8-10 TLVFILE class FI_OPEN method, 8-6 FL_COUNT method, 8-6 FL_DELREC method, 8-7 FL_READ_BY_TYPE method, 8-7 FL_REPLACE method, 8-7 FL_REWIND method, 8-6 FL_SENSE_REC method, 8-7 FL_SET_REC method, 8-6 FL_WRITE_REC method, 8-6 methods, 8-6 oop class, 8-4 TM_QABSOLUTE TIMER class method, 13-3 TO_ADD_DAYS TIME class method, 3-5 TO_ADD_MONTHS TIME class method, 3-5 TO_ADD_SECS TIME class method, 3-4 TO_ADD_YEARS TIME class method, 3-5 TO_GET_SYSDAT TIME class method, 3-7 TO_SENSE TIME class method, 3-4 TO_SENSE_FORMAT TIME class method, 3-6 TO_SET TIME class method, 3-3 TO_SET_FORMAT TIME class method, 3-6 type-length-value files, 8-4 VA_APPEND VAROOT class method, 5-4 VA_CAPACITY VAFLAT class method, 5-15 VAROOT class method deferred, 5-8 VASEG class method, 5-17 VASTR class method, 5-13 VA_COMPARE VAROOT class method, 5-5 VA_COMPRESS VAFLAT class method, 5-15 VAROOT class method deferred, 5-8 VASEG class method, 5-17 VASTR class method, 5-13 VA_COPY VAFIX class method, 5-10 VAROOT class method deferred, 5-7 VASTR class method, 5-14 VAXVAR class method, 5-21 VA_COUNT VAROOT class method, 5-4 VA_DELETE VAROOT class method, 5-5 VA_DELETEM VAFLAT class method, 5-15 VAROOT class method deferred, 5-8 VASEG class method, 5-17 VASTR class method, 5-13 VAXVAR class method, 5-20 VA_FINDISQ VAROOT class method, 5-6 VA_INIT VAFLAT class method, 5-15 VAROOT class method deferred, 5-8 VASEG class method, 5-17 VASTR class method, 5-12 VAXVAR class method, 5-20 VA_INSERT VAROOT class method, 5-4 VA_INSERTISQ VAROOT class method, 5-6 VA_INSERTM VAFLAT class method, 5-15 VAROOT class method deferred, 5-8 VASEG class method, 5-18 VASTR class method, 5-13 VAXVAR class method, 5-20 VA_KEY VAROOT class method, 5-5 VA_PBUF VAFLAT class method, 5-16 VAROOT class method deferred, 5-9 VASEG class method, 5-18 VASTR class method, 5-14 VAXVAR class method, 5-21 VA_PREC VAFLAT class method, 5-16 VAROOT class method deferred, 5-9 VASEG class method, 5-18 VASTR class method, 5-13 VA_RECLEN VAFIX class method, 5-10 VAROOT class method deferred, 5-7 VASTR class method, 5-13 VAXVAR class method, 5-21 VA_REPLACE VAFIX class method, 5-10 VAROOT class method, 5-7 VAXVAR class method, 5-21 VA_RESET VAROOT class method, 5-7 VA_SEARCH VAROOT class method, 5-6 VA_SORT VAROOT class method, 5-6 VA_SWAP VAFIX class method, 5-10 VAROOT class method deferred, 5-7 VA_TEST PSELVAR class method, 15-3 VAROOT class method, 5-5 VAXVAR class method, 5-20 VAFIX class methods, 5-10 oop class, 5-9 INDEX VA_COPY method, 5-10 VA_PREC method, 5-13 VA_RECLEN method, 5-10 VA_RECLEN method, 5-13 VA_REPLACE method, 5-10 VAXVAR class VA_SWAP method, 5-10 methods, 5-20 VAFLAT class oop class, 5-18 methods, 5-15 VA_COPY method, 5-21 oop class, 5-14 VA_DELETEM method, 5-20 VA_CAPACITY method, 5-15 VA_INIT method, 5-20 VA_COMPRESS method, 5-15 VA_INSERTM method, 5-20 VA_DELETEM method, 5-15 VA_PBUF method, 5-21 VA_INIT method, 5-15 VA_RECLEN method, 5-21 VA_INSERTM method, 5-15 VA_REPLACE method, 5-21 VA_PBUF method, 5-16 VA_TEST method, 5-20 VA_PREC method, 5-16 VAXVARS class VARIABLE ARRAY methods, 5-22 classes, 5-1 oop class, 5-21 VAROOT class DESTROY method, 5-4 methods deferred, 5-7 methods, 5-4 oop class, 5-3 VA_APPEND method, 5-4 VA_CAPACITY method deferred, 5-8 VA_COMPARE method, 5-5 VA_COMPRESS method deferred, 5-8 VA_COPY method deferred, 5-7 VA_COUNT method, 5-4 VA_DELETE method, 5-5 VA_DELETEM method deferred, 5-8 VA_FINDISQ method, 5-6 VA_INIT method deferred, 5-8 VA_INSERT method, 5-4 VA_INSERTISQ method, 5-6 VA_INSERTM method deferred, 5-8 VA_KEY method, 5-5 VA_PBUF method deferred, 5-9 VA_PREC method deferred, 5-9 VA_RECLEN method deferred, 5-7 VA_REPLACE method, 5-7 VA_RESET method, 5-7 VA_SEARCH method, 5-6 VA_SORT method, 5-6 VA_SWAP method deferred, 5-7 VA_TEST method, 5-5 VASEG class methods, 5-17 oop class, 5-16 VA_CAPACITY method, 5-17 VA_COMPRESS method, 5-17 VA_DELETEM method, 5-17 VA_INIT method, 5-17 VA_INSERTM method, 5-18 VA_PBUF method, 5-18 VA_PREC method, 5-18 VASTR class methods, 5-12 oop class, 5-11 VA_CAPACITY method, 5-13 VA_COMPRESS method, 5-13 VA_COPY method, 5-14 VA_DELETEM method, 5-13 VA_INIT method, 5-12 VA_INSERTM method, 5-13 VA_PBUF method, 5-14