TweetFollow Us on Twitter

IAC Driver Demo
Volume Number:4
Issue Number:11
Column Tag:Advanced Mac'ing

IAC Driver Demo in MPW C

By Frank Alviani, Contributing Editor, Waukegan, IL

The Great IAC Sample Editor!!

(or a reasonable facsimile thereof...)

Frank Alviani

I had just crawled out of my sickbed and back to work. After getting the IAC Driver article to Dave Smith at MacTutor in the nick of time (as usual), wrestling with thousands of lines of Pascal was starting to look like a little rest - when who calls but our beloved assistant editor, Kirk.

“How’s the next article coming, Frank? Gonna have it ready in time - maybe even a little early (wistful sigh)?” “No problem, Kirk”, I assured him; I had already set aside the evening of the deadline to get it written. I would have preferred to be out dancing with Shellie and Alice like Paul, but an editor’s gotta do what an editor’s gotta do....

Before continuing with the actual meat of this article, please understand that I assume you have read (and hopefully understood) the previous IAC articles (in the August and October ’88 issues of MacTutor). If you haven’t, I’m afraid this is going to be quite incomprehensible...

The IAC Sample Editor was written to demonstrate how to implement most (though not all) of the capabilities provided by the SAWS IAC driver. I chose to use a text editor for the same reasons many others have chosen to write sample text editors - TextEdit handles the surprisingly groaty details of handling the characters, allowing me to concentrate on the other aspects of the application. It handles a few small “frills”, such as choosing fonts and text sizes, because those are almost free; more complex enhancements such as scrolling have been left as “an exercise for the student”. THE CRITICAL MATERIAL IS THE IAC HANDLING, NOT THE TEXT HANDLING. [Window updating of the displayed text could use some work, but since we have published fully functional text editors in the past, the reader is referred to those articles for improvements to the text editing portion of the program. For purposes of this article, the ability to link documents betweeen two of these editors running together is what is important. A change in the linked text in one document, causes the linked text in another document to change automatically, thus creating integrated application software between two independent applications! -Ed]

Like most software, this sample editor is not 100% perfectly debugged (my employer has this delusion that I should be producing code for him during the day rather than debugging software for MacTutor), but all the important pieces are reasonably functional. The functions that are known to have holes are:

1) Auto-linking between two previously linked documents is somewhat unreliable. Alternative methods of accomplishing this are discussed.

2) Identification of source and target extents in the “Links” menu is occasionally reversed.

Changes To The IAC Driver

During the course of debugging this sample editor, the “Complete Dependency” routine was found to be excessively complex and slightly buggy. After revision [found on this month’s source code disk, -Ed], the rules it follows for handling slot-IDs are much simpler:

1) Until a document registers as a data source, its slot is filled with a “dummy” value of 1 to hold its place and indicate “target only” status (it doesn’t have a document ID yet).

2) When a target document does register as a data source, its slot is refilled with its assigned document ID.

In practice, this change would have very little effect on writing an application. The only real difference is that it is always a good idea to start a session with a slot-ID of 0 and let the IAC driver assign a slot, rather than trying to re-register under a slot-ID from the previous session.

Basic Additional IAC Necessities

The fundamental additional task facing an application using the IAC driver is that of keeping track of its data extents. You, as the source of data to other programs, must monitor each source of data in your document(s) and write any changed extents to the driver for the use of your client applications.

A change in a data extent can involve 2 factors: the contents of the extent, and its “size”. Monitoring changes can vary widely in complexity, where most of the complexity involves monitoring the “size” of the data extents. For example, in an object-oriented graphics program such as MacDraw, the “size” of a data-extent (a graphic object) is unlikely to change under most circumstances, and so involves rather little effort to maintain normally. On the other hand, almost any operation in a text-processor is likely to change the size of an extent, and therefore involves quite a bit of effort to keep up-to-date.

Since most programs are at least as interested in using data as in generating it, you are likely to also be a client (or dependent) of one or more other programs. In this case, it is your responsibility to check with the driver periodically to see if any data sources you depend on have changed, and to down load the data if they have. You need to maintain the link “targets” so that they can be updated as new information becomes available.

Let’s spend a few moments looking at how some basic tasks are done:

Checking for Updates - A client program is expected to periodically poll the IAC driver to determine if any of its source extents have been updated. Being a periodic action, this naturally fits into the main event loop. I recommend doing it during null-event processing, since you aren’t interfering with other activity. A possible concern with this approach is that of loading down the system by continuously polling the driver, and slowing down the normal response time; in the sample editor, this polling is done only at 1 second intervals. The only negative to this is that there can be up to 1 second before client applications respond to an update.

Determining when to update the IAC driver - this is the server’s side of the cooperation required. In the sample editor, I decided that the user would find the time needed to update the driver between keystrokes interfering with its usability, so the driver is updated only when you leave an extent [Note: in this version, only clicks outside an extent are checked - an extension for the future would be to also check the arrow keys.] In some applications (such as a real-time mail system) 1 update/keystroke might be exactly what is needed.

The ideal to strive for is to update the shared data as often as possible without sending meaningless intermediate data and without spending excessive time during the update process. Transmitting each digit to a spreadsheet as it is typed, for example, would be undesirable since recalculation might very well be triggered for each keystroke, rather than at the end when the entire number is sent.

Maintaining Extents - The simplest approach seems to do this on the fly. As mentioned above, changes to either the contents or the “size” of an extent need to be recorded. The sample editor in fact simply assumes that if you are leaving a source extent then you were probably doing something in it, and updates the driver. A more sophisticated approach would be to maintain a “dirty” flag for each extent, and update the IAC driver only if it was true.

Any operation that can affect the “size” of an extent determines the amount of change and adjusts the extent accordingly.

Providing a User Interface - In keeping with the visual nature of the Macintosh, there should be a visual method of making and displaying links between documents, that is consistent with the user interface guidelines Apple has developed. Simply typing in a starting and ending character position in a text editor, for example, is utterly unacceptable. The “magic clipboard” metaphor - a clipboard between applications that updates itself without intervention from the user - provides a logical extension that can be further developed.

User Interface Implementation

Most of the user-interface aspects of the IAC were implemented using menu commands. I chose this approach because it is familiar to all Mac users and independent of the application type.

All commands for starting and completing dependencies were placed on the Edit menu, since I am using the “magic clipboard” metaphor to make inter-application communications seem more familiar to the user. The “kill link” command is on the Edit menu simply because it is the location most of us would expect.

The method for displaying the location of IAC links is modeled after that of MPW: a menu with 1 menu item for each link in the document. The menu is not created until the first link exists. This approach was taken for several reasons. The most important implementation reason was that the ROM TextEdit package doesn’t support discontinuous selections - if it did, a discontinuous selection together with an alternate highlighting routine would be terribly simple and could show all links at once. Lacking that, the menu seems familiar and straightforward; the only non-obvious detail is that the link will remain highlighted until the first mouse click.

A dialog box is used by the “Show Link Info” command to show the results of a census call, to find all the dependencies currently recorded by the IAC driver. Only the information returned by the census (document ID, hat check, and edition) is displayed; “size” and content info is likely to be incomprehensible to most applications other than the originator.

Fundamental Data Structures

In addition to the algorithms all programs implement, the other critical issue in programming is the selection of data structures. In an application supporting the IAC driver, 2 additional data structures need to be implemented beyond those needed for the application’s work: a table of entries for each data extent (simply called the extent table from now on), and a small group of items that apply to a document as a whole, rather than to any particular individual extent.

Each entry in the extent table holds the “location” or “size” of the extent so that the application can determine when its contents have changed; how this is done is totally application-dependent. This “location” is also necessary so that you can determine when the user is entering and leaving an extent; as mentioned above this is important in deciding when to update the IAC driver. It is critical to record the source document_ID of an extent in order to know where you are the source or a target, and to identify the extent to the driver. The hat_check is also needed for identification. The last edition read of the extent is also needed so you can be sure to request its successor. A “dirty flag” would be a useful extension to further optimize sending data to the driver.

A certain amount of data needs to be kept about the document as a whole. This includes the slot_ID for the document, the document_ID, the number of extents for the document, and a handle to the extent table (although an application that would never handle more than one document could make the extent table a global data structure rather than allocating it on the heap).

Program Structure and Style

The IAC Sample Editor was developed in MPW C using MPW 2.0.2 on a 1-Megabyte MacPlus. This was done both because my development environment at home is identical to my environment at work, and because I am generally quite happy with MPW (with one caveat mentioned below).

The source code for the sample editor is broken into a number of files, for several reasons. The first reason is philosophical: working with a number of small, well-named files is much better when it comes to maintaining code. The second reason is pragmatic: this approach usually results in faster development, since far less of the source is processed when a change is made. The final reason is also pragmatic: it was the only way I could get the sample to build on my 1-Meg machine at home.

This brings up the caveat about MPW mentioned above - its memory appetite. The amount of memory needed for the C compiler seems rather excessive (especially compared to the Pascal compiler), and the overall requirements for MPW are high (1300K under MultiFinder, for example). While I am painfully aware of how much effort can be required to squeeze programs into smaller spaces, the cost of RAM is unlikely to drop a great deal in the near future, and requiring people to have 2-4 megabytes of RAM to use MPW 3.0 with source level debugging borders on arrogance. (End of tirade...)

Since this program was explicitly written as a sample to demonstrate how to use the IAC driver, it is somewhat more verbose than the traditional C program. I am convinced by experience that well-chosen names can be critical to the readability of code in any language, and try to write code that conveys as much meaning as possible at first glance. The argument that “C is a terse language” is simply concealed laziness; the slight extra effort involved in typing longer names will be repaid many times when you are debugging a routine, it is no longer fresh in your mind, and you don’t need to rummage around in many different source files to try and recall what those obscurely name variables are really being used for.

Please note that the IAC routines are careful to return error codes for the use of the application. While this sample doesn’t check them extremely thoroughly (nor does it check the ROM routine error codes as well as it should), a real applications should be paranoid about problems and pay attention to them!

The sample editor currently will support only 1 window. However, in order to simplify extension to multiple windows, all information relevant to an entire document (rather than just to individual extents) is maintained in a relocatable block whose handle is kept in the wRefCon field of the window record. It should not be very complex to extend the editor to properly support multiple windows.

The source files that make up the IAC Sample Editor, and their uses, are as follows:

Editor.b The MPW script that controls the building process.

Editor.m The input to “Make” that describes the dependencies between source files, and how to build any part of the Editor.

Editor.h Globals and common definitions.

IAC.h Definitions for the IAC interface package.

Editor.c The mainline, and the extent-maintaining routines.

Editor.r The Rez input file.

Boxes.c Code for displaying the dialog boxes, including the census information.

Doc_rtns.c Code for document management - open, close, save, etc.

Edit_IAC_rtns.c Code for implementing “Hot Copy” and “Hot Paste” operations.

Edit_rtns.c Code for implementing the standard text editing functions, with extensions for extent maintenance.

IAC.c The interface package to the IAC driver.

SysEnvs.a Glue for the SysEnvirons call.

While it would be tedious to read (and write!) a detailed description of every function in the program, certain functions do merit examination, since they demonstrate methods for using the IAC driver. Let’s take a closer look at these routines.

Extent-maintaining Routines

The operations necessary to maintaining extent information have been collected into a group of routines which are in the main file Editor.c These are as follows:

chk_extent: This routine is used by all the other extent- maintaining functions. In this text editor, an extent is defined to be a character range (in others it could be a paragraph, for example). If the current selection overlaps an entry in the extent table, that extent is made the current extent. We check the current extent first, as a small optimization to try and avoid processing the entire extent table. In any case, we return a boolean that tells if we are now in an extent.

One limitation in this implementation is that it does not deal with a selection range that spans extents. In a real application an extent very likely would be a significant amount of data (rather than a single word, for instance), and a selection would be much less likely to span extents.

This is a function that is very application dependent, and will vary a lot in implementation, since the tests for a graphics program is utterly different from those for a text program.

ext_move: This is the routine that determines if the user has left the current extent, and updates the IAC driver if he/she has. If the selection is no longer in the original extent and that extent was a “source” extent (one for which we are sending data to the IAC driver), then we write the latest contents of that extent to the driver. We have nothing to do if we are transferring into an extent, or if the extent we are leaving is a “target” extent.

This does not maintain a “dirty” flag for each extent, and assumes that any source extent you are leaving has been changed, writing its data to the driver automatically. Maintaining a dirty flag for each extent (as well as for the document as a whole) would allow this routine to minimize traffic to the IAC driver.

ext_write: This routine is responsible for writing the contents of an extent to the IAC driver. It sets up a private buffer into which the data size, constant format of ‘TEXT’, and the data are written, and then just calls the interface routine iac_write_data.

ext_read: This routine is responsible for read the contents of an extent from the IAC driver. It automatically asks for the successor to the last extent read, and updates the extent with the edition actually returned by the driver. The selection range of the extent is selected and replaced by the new data from the driver. If there was a change in the size of the extent, the extent is updated.

IAC User-Interface Routines

These are the routines that are the user’s actual interface to the IAC driver. They are as follows:

do_hotCopy: This is the routine that establishes a “source” link. The first step is to check to see if we are already in an existing dependency; if we are, an alert is issued to the user pointing out that error, and we quit. Normal processing is to call iac_add_dependency to add an entry to the driver’s dependency table. Since source documents are issued document IDs, this updates the doc_ID field of the window data block. Since the slot_ID field can be updated by the driver, it is also updated by this routine. A new entry is added to the table of extents associated with the document.

After the dependency has been successfully started, this routine also calls ext_write to send the initial copy of the contents of the extent to the driver (we assume that creating a link implies that another program will be immediately interested in the contents).

In order to give the user some way of viewing the links established between documents, add_display_cmd is called to add the newly established dependency source to the “Links” menu.

do_hotPaste: This is the routine that completes a link. Like do_hotCopy, the first step is to check to see if we are already in an existing dependency; if we are, an alert is issued to the user pointing out that error, and we quit. Normal processing is to call iac_complete_dependency. We do not update the document ID field since the document ID returned by the driver is that of the source document. Since the slot_ID field can be updated by the driver, it is updated by this routine. A new entry is added to the table of extents associated with the document.

After the dependency has been successfully completed, this routine calls ext_read to read the initial copy of the contents from the driver.

In order to give the user some way of viewing the links established between documents, add_display_cmd is called to add the newly established dependency target to the “Links” menu.

The two IAC routines use different algorithms for adding a new entry to the extent table, which was done for a reason. In some applications, the ordering of extents may not be important; do_hotCopy makes this assumption and just adds another extent to the end of the table. In other applications (such as a text editor) the order may be more relevant; do_hotPaste inserts a new entry so the starting locations of the extents are in ascending order. In this text editor, this means that only an extent and its successors need updating, rather than having to process the entire table. In a real application, of course, you need to take a consistent approach to maintaining the extent table.

Text Editing Routines

These perform the normal TextEdit functions, with the addition of the IAC code. Since development time was stretching out well beyond expectation, only the routine do_key actually updates extent-size information, but all of the routines that need to do so have the skeleton code in place, and all of them do actually communicate with the IAC driver.

do_clear: This uses the normal TEDelete call to the ROM to remove text without placing it on the clipboard, after calling chk_extent to see which , if any, extent is being affected. If an entire extent was cleared, iac_remove_dependency is called, otherwise iac_write_data is called to provide the latest data to the driver.

do_copy: This does nothing at all with the IAC driver, since it doesn’t affect the data!

do_cut: The processing here is virtually identical to that in do_clear, except that TECut is called instead.

do_key: We check to see if we are in an extent ( chk_extent), and then call TEKey to actually insert the character into the text. If we were in an extent, we only need to adjust the end of the extent. If a backspace was processed, we decrement the end, otherwise we increment it.

do_paste: This is very similar in processing to do_key. If we were in an extent, the end of the extent is incremented by the amount of text inserted. This is actually an oversimplification, since it doesn’t take into account text replacement.

Document Maintaining Routines

These perform all the traditional file-system functions, with the addition of saving and restoring IAC information needed for the convenience of the user. They are not in alphabetical order:

setup_wind: This is a common routine is used to set up the window and initialize the extent table and window data structures.

save_doc: In addition to the normal function of writing the data to a file, creating it if necessary, this must also save the extent table and the document-wide data for future usage. This information is stored in resources of type ‘EXTN’; if we are saving an existing file, previous resources are removed before writing the new versions.

open_doc: While opening a document is often a complex process due to the application’s data structures, in the sample editor the complexity is due to the (optional) process of trying to reconnect any links that may have existed to already-open documents. This is half of the reconnection process; the other half, of having an open document reconnect to a just-opened document, would be a function of poll_iac.

The functions of this routine divide neatly into several parts:

(1)read the text data from a user selected file and create a window for it.

(2)retrieve the extent table and some of the document-wide data and attach them to the window.

(3)once these have been retrieved, try to re-establish any links that had existed previously, with already-opened documents. This is done as follows:

a) Issue an iac_census call to determine the current state of the universe. Assuming the call is successful and there are already documents that have posted their source dependencies,

b) Check each entry in the census against the extent table. If a census entry matches an extent:

i) issue an iac_available_dependency call to setup the linkage,

ii) issue an iac_complete_dependency call to complete it,

iii)issue an ext_read call to get the latest copy of the data from the driver, and

iv)call add_display_cmd to add the dependency to the user’s “Links” menu.

c) Now that as many as possible of your target extents are linked up again, it’s time to post your source extents to the IAC driver. This is a simpler process, since it isn’t necessary to match the census info against the extent table. For each extent just:

i) If you are the source for the extent, call iac_add_dependency to post the extent to the IAC driver,

ii) call ext_write to copy the latest version of your data up to the driver, and

iii)call add_display_cmd to add the dependency to the user’s “Links” menu.

This is one approach to reconnecting documents that were linked in a “previous life”. The other approach is roughly equivalent in complexity, but moves part (b) in the description above to poll_iac. Open_doc simply posts all of its source extents, and poll_iac recognizes any new sources that appear during a census. When a new source document appears, it then reconnects.

Other Routines

These serve important functions, but don’t fit neatly into the other categories. They are as follows:

poll_iac: This is responsible for automatically downloading updated data from the IAC driver. It checks at 60-tick (1 second) intervals to balance timely updates against system traffic. iac_status is called to see if any changes at all have occurred.

There are several strategies that can be used to ensure that all updated links are read. The approach used here is simply to try and read every link for which it is a target, relying on the driver to ignore any extents which haven’t been changed. Another, probably more efficient tack, is to make an iac_census call first. You would then check the edition of each source link against that stored in the extent’s record, and only try and read those which you know have changed. The trade off is in the (slightly more complex) processing required for the census call against the (simpler) unnecessary read calls.

An extension mentioned above that can be added to this routine is to check the data returned by the iac_status call to see if a new document has added dependencies to the driver. If one has, you can then make an iac_census call to check the new entries against your extent table; should any of them be sources for your target links, those links can be re-established without further intervention. This is needed if an open document is to be able to re-link to a document opened after it.

link_display: This is used to highlight an extent. It uses a tiny assembler routine to implement an alternate highlighting routine as (obscurely) documented in the TextEdit chapter of Inside Macintosh. The routine is installed, and the extent range selected, which invokes our highlighting routine (which uses exclusive-or mode for reversibility). We then check the mouse button in order to wait for a mouse click to indicate the user is done, and reselect the original range (which calls our routine, which undoes the highlighting). Finally, the hook in the TE record is removed to restore TextEdit’s normal behavior.

The only small difficulty came in getting the assembler routine linked in with the rest of the application. It turns out to be necessary to have the assembler’s case option set to OBJECT so that the procedure label is written to the object file with the proper case (C is case sensitive), but to allow case-insensitivity for references to equate files. Sigh...

Debugging Concerns

Debugging under MultiFinder is slightly different than debugging under the normal Finder, primarily due to the ease with which you can get “lost” in memory. The latest version of TMON (2.8.1- which is what I always use for debugging), for example, has no facilities for dealing with multiple application zones. This is not the end of the world, however.

The primary tool at your disposal is attention: you must pay closer attention to the application zone than under the single-application system, since the only way to tell zones apart is by their address ranges. Fortunately, all of your regular debugging tools still work, and context switching occurs only at well-specified times, so life really isn’t too bad once you get used to things.

The symbolic-debugging ability of TMON is virtually invaluable, and requires one small change to the normal C compilation. The “-g” option is required to have the MPW C compiler insert the stack frames and routine names needed by TMON, since the normal code generated doesn’t require or use them.

It is often useful to set breakpoints in several applications at once. TMON supports this with no difficulty; the only trick is to be absolutely certain of which application is in the foreground when you set the breakpoint. I often start setting breakpoints in one program from the left end of the window, and breakpoints in the other program from the right end; this can sometimes make it a little easier to tell what’s going on.

Final Notes

This has turned out to be a far larger piece of cake to chew than either Paul or I originally anticipated (merely proving again that all programmers are optimists, or we never would have gotten ourselves into all this ) and has also turned out to be very educational, to say the least. While this is the last article on the IAC I will be writing for a while (I am in the middle of massively renovating my house as this is being written), at least one more article that I know of should be appearing in the near future. Once my brain has cleared somewhat, there are several non-obvious extensions that can be implemented even with the driver in its current state, and a number of people have already discussed adding inter-machine communications. The future should be most interesting.

I cannot end this escapade without expressing my deepest gratitude to my infinitely-patient wife Alice, who has put up with a tired 1-year old by herself far too many times while I was up into the early morning trying to wring bugs out of stubborn code.

Continued in next frame
Volume Number:4
Issue Number:11
Column Tag:Advanced Mac'ing

IAC Driver Demo in MPW C (code)

{1}
Listing:  Editor.b

###     Editor.b - Build the sample editor
#
#  Usage:
#Set exit [0 | 1];
#If exit == 0, all compiles and assemblies will be completed.  If any 
errors occur, subsequent steps will be skipped. If exit == 1, the procedure 
stops after the first error occurs.
#
#Set hlxSrc “source directory”; Export hlsSrc
#Set hlxEtc “result directory”; Export hlsEtc
#Set hlxProg “auxiliary program”; Export hlsProg
#  general.b [ -r | -m] [makeParams ]
#  #  -rRepeat previous build, using existing “{hlxEtc}makeout”. (Actually, 
this option merely suppresses the “makeout” creation.)
#  #    Must be the first (and only) parameter.
#  #  -mMake “{hlxEtc}makeout”, but do not execute it.
#  #    Must be the first parameter.
#  #  makeParams Additional parameters to make; e.g.:
#  #    -d pr=”-p” Report progress
#  #    -d e=”#” Suppress log detail
#  #    -eMake everything.
#  #    -tMerely touch dates to bring everything up-to-date.

open “{log}”
#open “{worksheet}”
echo ‘Date -a -t‘ ‘Date -s -d‘ Start {hlxAlt} >> “{log}”

cd “{hlxSrc}”
set result Done
if “{1}” == “-m”
 set result Made
 shift 1
end
if “{1}” <> “-r”
 If {result} == Done
 echo set err 0 > “{hlxEtc}makeout”
 unset err
 make {“parameters”} -f {hlxprg}.m >> “{hlxEtc}makeout”
 end || Set result Failed
end
if {result} == “Done”
 “{hlxEtc}makeout” || Set result Failed
end
echo ‘Date -a -t‘ ‘Date -s -d‘ {result} >> “{log}”
{MacAppDone}
exit 1 If result == Failed
{2}
Listing:  Editor.m

# Make file for the Sample IAC-friendly Editor

ob = {hlxEtc}    #Set these definitions to your normal working directories
sr = {hlxSrc}
prog = {hlx}
e  = echo 

COptions = -o {ob} -g -q2
AOptions = -i {mpw}AIncludes -o “{ob}”

“{prog}Editor”   ƒ Editor.r {ob}Editor.code
 {e} ‘Date -a -t‘ Rez Editor >> “{log}”
 Rez  Editor.r -o “{prog}Editor” -t APPL -c IAC1
 Setfile -a b “{prog}Editor”#set bundle bit

{ob}Editor.code  ƒ {ob}Editor.c.o  {ob}IAC.c.o 
 {ob}SysEnv.a.o  {ob}Doc_rtns.c.o 
 {ob}Edit_IAC_rtns.c.o  {ob}Edit_rtns.c.o 
 {ob}Editor.a.o  {ob}Boxes.c.o
 exit 1 if {err} <> 0
 {e} ‘Date -a -t‘ Link Editor >> “{log}”
 Link {ob}Editor.c.o {ob}IAC.c.o   {ob}SysEnv.a.o 
  {ob}Doc_rtns.c.o {ob}Edit_IAC_rtns.c.o     {ob}Edit_rtns.c.o 
  {ob}Editor.a.o {ob}Boxes.c.o 
  “{CLibraries}”CRuntime.o 
  “{CLibraries}”CInterface.o 
  “{CLibraries}”StdCLib.o 
  “{CLibraries}”CSANElib.o 
  “{Libraries}”Interface.o 
  -t “OBJ “ -o {ob}Editor.code

{ob}Editor.c.o ƒ {sr}Editor.c {sr}IAC.h{sr}Editor.h
 {e} ‘Date -a -t‘ C Editor >> “{log}”
 C {COptions} Editor.c || set err 1

{ob}IAC.c.o ƒ  {sr}IAC.c  {sr}IAC.h
 {e} ‘Date -a -t‘ C IAC >> “{log}”
 C {COptions} IAC.c || set err 1

{ob}SysEnv.a.o ƒ {sr}SysEnv.a
 {e}  ‘Date -a -t‘ Asm SysEnv >> “{log}”
 Asm {AOptions}  SysEnv.a || Set err 1

{ob}Editor.a.o ƒ {sr}Editor.a
 {e}  ‘Date -a -t‘ Asm Editor.a >> “{log}”
 Asm {AOptions}  Editor.a || Set err 1

{ob}Doc_rtns.c.o ƒ {sr}Doc_rtns.c  {sr}IAC.h {sr}Editor.h
 {e} ‘Date -a -t‘ C Doc_rtns >> “{log}”
 C {COptions} Doc_rtns.c || set err 1

{ob}Edit_IAC_rtns.c.oƒ    {sr}Edit_IAC_rtns.c      {sr}IAC.h   {sr}Editor.h
 {e} ‘Date -a -t‘ C Edit_IAC_rtns >> “{log}”
 C {COptions} Edit_IAC_rtns.c || set err 1

{ob}Edit_rtns.c.oƒ {sr}Edit_rtns.c {sr}IAC.h {sr}Editor.h
 {e} ‘Date -a -t‘ C Edit_rtns >> “{log}”
 C {COptions} Edit_rtns.c || set err 1

{ob}Boxes.c.o    ƒ {sr}Boxes.c{sr}IAC.h{sr}Editor.h
 {e} ‘Date -a -t‘ C Boxes >> “{log}”
 C {COptions} Boxes.c || set err 1
{3}
Listing:  Editor.h

/*
 * Resource ID constants.
 */
# defineappleID  128 
# definefileID   129
# defineeditID   130
# defineoptionsID131
# definefontID   132
# definesizeID   133
# definelinkDisplayID134

/* MyMenus[] array indexes */
# defineappleMenu0
# defineaboutMeCommand  1

# definefileMenu 1
# definenewCommand 1
# defineopenCommand2
# definecloseCommand 3
# definesaveCommand4
# definequitCommand  6

# defineeditMenu 2
# defineundoCommand  1
# definecutCommand 3
# definecopyCommand  4
# definepasteCommand 5
# defineclearCommand 6

/* The IAC Commands!! */
# definehotCopyCommand  8
# definehotPasteCommand 9
# definezapLinkCommand  10

# defineoptionsMenu3
# defineshowLinksCmd 1
# defineshowLinkInfoCmd 2

# define fontMenu4
# define sizeMenu5
# define linkdDispMenu    6
# define menuCount 7

/* windows, dialogs, and alerts */
# define windowID128
# define ABOUT_DLOG128
# define ABOUT_LINKS 129
# define NO_IAC  256
# define IAC_ERR_ALRT257
# define NOT_IN_EXT258
# define KILL_EXT259
# define SAVE_CHANGES260

# define BS 0x08
# define POLL_INT 60
# define SLEEP 20

/* Formats supported by IAC driver, normal resource types */
# define TXT_FMT 0x54455854

/* Error codes returned by the IAC driver */
# define NO_MORE_DOCS -2000
# define NO_MORE_SLOTS -2001
# define WRITE_FAILED -2002
# define MISSING_LINK -2003
# define NO_NEWER_ED -2004
# define READ_FAILED -2005
# define NO_SUCH_DEP -2006
# define OLD_ROMS -2007

/*
 * Information records associated with a window
 */

typedef struct {
 long   src_doc; /* doc_ID of source doc */
 short  hat_check; /* identifier for an extent */
 short  ed_level;/* edition for extent */
 short  ext_strt;/* start of extent range */
 short  ext_end; /* end of extent range */
} extent, *extentP, *exTable, **extentH;

typedef struct {
 short  the_slot;/* slot_ID for this document */
 long   doc_ID;  /* document ID for this doc */
 short  ext_cnt; /* number of extents with this doc */
 short  relevent;/* # un-updated extents */
 extentHthe_extents; /* block of extent records */
 TEHandle wind_TEH;/* TEHandle for this window */
 Booleandirty;   /* doc needs saving */
 String(63) doc_file_nm;  /* if null, never been saved */
} win_data, *win_dataP, **win_dataH;

/****
 * Global Data objects, used by routines external to main().
 ***/

#ifndef PUBLIC
#define PUBLIC
#undef NO_INIT
#else
#define NO_INIT
#endif

PUBLIC MenuHandleMyMenus[menuCount]; /* The menu handles */
PUBLIC Boolean   DoneFlag;/*true when File:Quit chosen */
PUBLIC TEHandle  TextH;   /* The TextEdit handle */
PUBLIC CursHandleibeamHdl;
PUBLIC WindowPtr myWindow;/* the text window */
PUBLIC WindowRecordwRecord; /* store the window stuff */
PUBLIC win_dataH the_data_H;/* data for a window */
PUBLIC shortfRef;/* refnum for document file */
PUBLIC shortthe_fNum, the_size;    /* text attributes */
PUBLIC Stylethe_style;
PUBLIC extent    curr_ext;/* latest extent worked on */
PUBLIC shortcurr_ext_no;  /* index of curr_ext */
PUBLIC Boolean   ext_active;/* an active extent? */
PUBLIC long last_poll;    /* when we last polled IAC */

/* f_sizes & f_styles are used in the menu_tree() processing and are 
read only */

#ifdef NO_INIT
PUBLIC shortextent_count; /* links we are target of */
PUBLIC Boolean   link_menu; /* link menu created */
PUBLIC shortf_sizes[8];
PUBLIC Stylef_styles[5];
#else
PUBLIC shortextent_count = 0;
PUBLIC Boolean   link_menu = false;
PUBLIC shortf_sizes[8] = {9, 10, 12, 14, 16, 20, 24, 28};
PUBLIC Stylef_styles[5] = {bold, italic, underline, outline, shadow};
#endif

/*
 * HIWORD and LOWORD macros, for readability.
 */
# define HIWORD(aLong)    (((aLong) >> 16) & 0xFFFF)
# define LOWORD(aLong)    ((aLong) & 0xFFFF)
{4}
Listing:  IAC.h

/* This is the set of externs needed to access the IAC interface routines.
 The pointer/handle indicators are only reminders */

extern  short  iac_add_dependency();/* *doc_ID, *slot_ID, *hat_check, 
*edition */
extern  short  iac_available_dependency(); /* doc_id, hat_check */
extern  short  iac_census();/* *extent_count, **extent_info */
extern  short  iac_complete_dependency();    /* *doc_id, *slot_id, 
 *hat_check */
extern  short  iac_open();
extern  short  iac_read_data();  /* doc_id, slot_id, hat_check, 
 *edition, fmt_pref[], *fmt_code, **ext_data */
extern  short  iac_remove_dependency();/* doc_id, slot_id,     
 hat_check */
extern  short  iac_status();/* slot_id, *vers_id, *doc_count,
 *extent_count */
extern  short  iac_write_data(); /* doc_id, hat_check, *edition,
  fmt_count, **ext_data */

/* error codes for iac_open, etc. */
# define NO_DRIVER -1
# define EARLY_SYS -2
# define MAX_EXTS64

typedef struct {
 long   doc_ID;
 short  ed_level;
 short  hat_check;
}info_rec;

typedef struct {
 info_rec ext_entry[MAX_EXTS];
} info_tbl, *info_tblP, **info_tblH;
{5}
Listing:  Editor.a

;**************************************************************
;*****  The SAWS Inter-Application Commun. Driver Sample Program
;*****  Text Highlighting Routines for TextEdit
;*****  Frank Alviani - 9/88
;**************************************************************

 INCLUDE‘Traps.a’
 INCLUDE‘QuickEqu.a’
 INCLUDE‘SysEqu.a’
 INCLUDE‘SysErr.a’
 INCLUDE‘ToolEqu.a’
 STRING PASCAL
 CASE OBJECT

;
;This routine highlights the rectangle passed on the stack by
;“stippling” it. There could be separate routines for“target”
;and “source” links. Exclusive-or mode is used since this 
; called twice (select and deselect) and we need to return the 
; text to its original look.
;Input: A3 points to locked TERec
;Can destroy A0, A1, D0, D2, D3
;
SourceHighPROC EXPORT
 MOVE.W #patXor,-(A7);set mode
 _PenMode
 MOVE.L GrafGlobals(A5),A0
 PEA  ltGray(A0) ;set stipple
 _PenPat
 _PaintRect ;stipple the text
; _PaintRect pops rectangle off stack, leaving return address
 RTS

 END
{6}
Listing:  Editor.c

/***
 *
 * File:Editor.c
 *
 * Package: Mainline
 *
 * Description:  This is the mainline for the editor program to test 
the Inter Application Communications (IAC) driver. Some code from the 
Apple “sample.c” was adapted.
 *
 * Structure:  In this source the following structure is used:
 * includes & defines
 * global variable definitions
 * main()
 * initialization routines
 * the menu dispatcher menu_tree()
 * dialog-box handlers
 *
 * Author:
 * FEA  6/88 - 7/88
 */

# include <types.h>
# include <memory.h>
# include <quickdraw.h>
# include <toolutils.h>
# include <windows.h>
# include <controls.h>
# include <fonts.h>
# include <events.h>
# include <dialogs.h>
# include <menus.h>
# include <desk.h>
# include <textedit.h>
# include <segload.h>
# include <string.h>
# include <resources.h>
 
# include <iac.h>
# undef PUBLIC
# include <Editor.h>

#define noErr     0   /* 0 for success */

extern _DataInit();

/**
 * Routine: main()
 *
 * Mainline of the test program
 */

int main()
{
short   init_result; /* NULL if OK */
short   item;    /* alert button */
Rect    screenRect;
Rect    dragRect;
Rect    txRect;
Point   mousePt;
EventRecord myEvent;
WindowPtr theActiveWindow;
WindowPtr whichWindow;

win_dataH the_data_H;/* data associated with a window */

extern  void     menu_tree(), do_key(), poll_iac();
extern  voidext_move();
extern  short  edit_init();
extern  WindowPtrmyWindow;

 UnloadSeg(_DataInit);
 init_result = edit_init();
 if (init_result)
 {
 item = StopAlert(NO_IAC, nil);
 return 0;/* allow C runtime cleanup */
 }

 UnloadSeg(edit_init);    /* if we get here, IAC open */

 /* window setup is handled by ‘new’ and ‘open’ commands */
 screenRect = qd.screenBits.bounds;
 SetRect(&dragRect, 4, 20 + 4, screenRect.right-4, screenRect.bottom-4);

 /* The One True Event Loop */
 DoneFlag = false;
 for ( ;; )
 {
 if (DoneFlag)
 {
 break; /* from main event loop */
 }
 
 /*
  * Main Event tasks:
  */
  
 theActiveWindow = FrontWindow();  /* Used often, avoid repeated calls 
*/

 if (myWindow && (myWindow == theActiveWindow))
 {
 GetMouse(&mousePt);
 SetCursor(PtInRect(&mousePt, &myWindow->portRect) ? *ibeamHdl : &qd.arrow);
 TEIdle(TextH);

 the_data_H = (win_dataH) GetWRefCon (myWindow);
 if ((**the_data_H).dirty)
 {
 EnableItem (MyMenus[fileMenu], saveCommand);
 }
 else
 {
 DisableItem (MyMenus[fileMenu], saveCommand);
 }
 }
 
 if (!WaitNextEvent(everyEvent, &myEvent, SLEEP, nil))
 {
 /*A null or system event! IAC polling goes here.
 We only poll if we have link targets to check. */
 if (theActiveWindow && extent_count)
 {
 poll_iac(theActiveWindow);
 }
 continue;
 }

 the_data_H = (win_dataH) GetWRefCon(theActiveWindow);
 TextH = (**the_data_H).wind_TEH;
 
 switch (myEvent.what)
 {
 case mouseDown:
 switch (FindWindow(&myEvent.where, &whichWindow))
 {
 case inSysWindow:
 SystemClick(&myEvent, whichWindow);
 break;

 case inMenuBar:
 menu_tree(MenuSelect(&myEvent.where));
 break;

 case inDrag:
 DragWindow(whichWindow, &myEvent.where, &dragRect);
 break;

 case inGrow:  /* There is no grow box. */
 break;

 case inContent:
 if (whichWindow != theActiveWindow)
 {
 SelectWindow(whichWindow);
 }
 else if (whichWindow == myWindow)
 {
 ext_move(&myEvent, theActiveWindow);
 }
 break;

 default:
 break;
 }/*endsw FindWindow*/
 break;

 case autoKey: /* ignore command-key */
 if (myWindow == theActiveWindow)
 {
 do_key(myEvent.message); /* check extents */
 }
 break;

 case keyDown:
 if (myWindow == theActiveWindow)
 {
 if (myEvent.modifiers & cmdKey)
 {
 menu_tree(MenuKey(myEvent.message & charCodeMask));
 }
 else
 {
 do_key(myEvent.message); /* check extents */
 }
 }
 break;

 case app4Evt: /* suspend/resume */
 break;

 case activateEvt:
 if ((WindowPtr) myEvent.message == myWindow)
 {
 if (myEvent.modifiers & activeFlag)
 {
 TEActivate(TextH);
 DisableItem(MyMenus[editMenu], undoCommand);
 }
 else
 {
 TEDeactivate(TextH);
 EnableItem(MyMenus[editMenu], undoCommand);
 }
 }
 break;

 case updateEvt:
 if ((WindowPtr) myEvent.message == theActiveWindow)
 {
 BeginUpdate(theActiveWindow);
 EraseRect(&theActiveWindow->portRect);
 TEUpdate(&theActiveWindow->portRect, TextH);
 EndUpdate(theActiveWindow);
 }
 break;

 default:
 break;

 } /* endsw myEvent.what */

 } /* for */

 if (myWindow)
 {
 /* shut down code, including “save changes?” */
 CloseWindow(myWindow);
 }
 return 0;/* Return to allow C runtime cleanup */
}


 /**
 * Routine: chk_extent
 *
 * This routine checks the selection range in the TE record to determine 
if it intersects the current extent. If not, it checks the entire set 
of extents to try and identify the extent it might intersect. If we are 
notcurrently in an exent, the ‘current extent’ structure is filled with 
-1s as is ‘curr_ext_no’. */

# define __SEG__ Main
Boolean chk_extent(TextH, the_extH, ext_cnt)
 TEHandle TextH; /* The TextEdit record to check */
 extentHthe_extH;/* handle to extent block */
 short  ext_cnt; /* how many extents to process */
{
short   strt, endd;/* range in TE record */
short   i;/* scratch */
exTable ext_recs;
Boolean left, right;


 if (ext_cnt)
 { 
 strt = (**TextH).selStart;
 endd = (**TextH).selEnd;
 ext_recs = *the_extH;
/* Check last extent used. If not in that, scan entire table. We are 
affecting an extent if EITHER the start or end of the TE selection falls 
between the start and end of the extent (inclusive).  */

 left = (curr_ext.ext_strt <= strt) && (curr_ext.ext_end >= strt);
 right =(curr_ext.ext_strt <= endd) && (curr_ext.ext_end >= endd);
 if (left ||right) /* overlap on either end */
 {
 return (true);  /* existing info alright as is */
 }
 else   /* switched, check all */
 {
 for (i=0; i<ext_cnt; i++)
 {
 left = (ext_recs[i].ext_strt <= strt) && (ext_recs[i].ext_end >= strt);
 right = (ext_recs[i].ext_strt <= endd) && (ext_recs[i].ext_end >= endd);
 if (left ||right) /* this now the current extent */
 {
 curr_ext.ext_strt =  ext_recs[i].ext_strt;
 curr_ext.ext_end  =  ext_recs[i].ext_end;
 curr_ext.hat_check = ext_recs[i].hat_check;
 curr_ext.ed_level =  ext_recs[i].ed_level;
 curr_ext_no = i;
 return (true);
 }
 } /* end for */
 } /* end else */
 } /* end there-are-extents */

 curr_ext.ext_strt =  -1; /* not current in an extent */
 curr_ext.ext_end  =  -1;
 curr_ext.hat_check = -1;
 curr_ext.ed_level =  -1;
 curr_ext_no = -1;
 return(false);
}

/**
 * Routine: ext_write
 *
 * This routine handles the mechanics of actually writing an extent’s
 data to the IAC driver. */

# define __SEG__ Main
short ext_write(TextH, strt, sz, the_doc, the_hatcheck, the_ed)
 TEHandle TextH; /* The TextEdit handle */
 short  strt;    /* position of 1st char to write */
 short  sz; /* count of bytes */
 long   the_doc; /* which doc */
 short  the_hatcheck;
 short     *the_ed;
{
short   iac_code;/* result from IAC call */
long    data_size;
Handle  ext_data, t_base;
long    *l_ptr;  /* recast to ease setting up hdr */

# defineHDR_SIZE 8

 ext_data = NewHandle (sz + HDR_SIZE);
 l_ptr = (long *)(*ext_data);
 *l_ptr = TXT_FMT;
 *(l_ptr+1) = (long) sz;
 t_base = (**TextH).hText;
 BlockMove (*t_base + strt, /* source */
    (*ext_data) + HDR_SIZE, /* dest */
    (long) sz);
 iac_code = iac_write_data(the_doc, the_hatcheck, the_ed, 1,   ext_data);
 DisposHandle(ext_data);
 return (iac_code);
}

/**
 * Routine: ext_read
 *
 * This routine handles the mechanics of actually reading an extent’s 
data from the IAC driver. If successful the edition_level for the extent 
is updated. */

# define __SEG__ Main
short ext_read(w_Ptr, the_ed, which)
 WindowPtrw_Ptr;
 short      *the_ed; /* edition of the extent to read */
 short  which;   /* which extent (zero-based) */
{
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
TEHandleTextH;   /* The TextEdit handle */
exTable ext_recs;/* walking extents for adjusting */
long    fmt_code;/* actual data format from IAC */
long    fmt_pref[3]; /* format prefs, descending  */
long    old_st, old_end;  /* TE selection before poll */
short   delta;   /* change in extent size */
short   an_ed, j;/* traditional loop counters */
short   data_size;
Handle  ext_data;/* Handle to data block */

short   iac_code = noErr; /* result from IAC call */

 the_data_H = (win_dataH) GetWRefCon(w_Ptr);
 TextH = (**the_data_H).wind_TEH;
 the_extH =  (**the_data_H).the_extents;

 fmt_pref[0] = TXT_FMT;
 fmt_pref[1] = 0;
 HLock(the_extH);
 ext_recs = *the_extH;
 ext_data = NewHandle(0L);
 /* need memory test here */

 an_ed = *the_ed + 1;
 iac_code = iac_read_data(ext_recs[which].src_doc,
  (**the_data_H).the_slot,
  ext_recs[which].hat_check,
  &an_ed,
  &fmt_pref[0],
  &fmt_code,
  ext_data);

 if (iac_code == noErr)
 {
 ext_recs[which].ed_level = an_ed; /* update extent */
 *the_ed = an_ed;/* update caller */
 data_size = GetHandleSize(ext_data);

 delta = data_size - (ext_recs[which].ext_end -
  ext_recs[which].ext_strt);

 TESetSelect (ext_recs[which].ext_strt,/* select extent for TE */ ext_recs[which].ext_end,TextH);
 TEDelete(TextH);
 HLock(ext_data);
 TEInsert(*ext_data, data_size, TextH);/* update text */
 HUnlock(ext_data);
 
 ext_recs[which].ext_end = ext_recs[which].ext_strt + data_size;
 /* update end (start unchanged) */

 if (delta) /* only update if there was a change */
 {
 /* offset each following extent by change in size of this extent */
 for (j=which+1; j<(**the_data_H).ext_cnt; j++)
 {
 ext_recs[j].ext_strt += delta;
 ext_recs[j].ext_end +=  delta;
 }
 }
 }

 HUnlock(the_extH);
 DisposHandle (ext_data); /* clean up */
 return (iac_code);
}

/**
 * Routine: ext_move
 *
 * This routine checks to see if we have left the “current extent”. If 
 we have, we check to see if we are the source for that extent. If so,
 the contents of that extent are written to the IAC driver. If we are 
instead mousing INTO an extent, that becomes the current extent.
 */

# define __SEG__ Main
void  ext_move(myEvent, w_Ptr)
 EventRecord   *myEvent;
 WindowPtrw_Ptr;
{
EventRecord anEvent;
short   iac_code;/* result from IAC call */
OSErr   an_err;
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
exTable ext_recs;/* for updating old extent */
TEHandleTextH;   /* The TextEdit handle */
short   i, the_ed, old_ext_no;
short   data_size;
extent  old_ext; /* to check for change in extents */
Handle  ext_data, t_base;
long    *l_ptr;  /* recast to ease setting up hdr */

extern  Boolean ext_active;
extern  Boolean  chk_extent();

#define HDR_SIZE 8

 the_data_H = (win_dataH) GetWRefCon(w_Ptr);
 TextH = (**the_data_H).wind_TEH;
 the_extH =  (**the_data_H).the_extents;
 ext_recs =  *the_extH;
 old_ext_no = curr_ext_no;
 anEvent = *myEvent; /* local copy for modification */

 GlobalToLocal(&anEvent.where);
 TEClick(&anEvent.where, (anEvent.modifiers & shiftKey) != 0, TextH);

 if (ext_active) /* we may need to write it’s data to the IAC driver 
*/
 {
 old_ext = curr_ext;
 the_ed = curr_ext.ed_level;
 ext_active = chk_extent(TextH, the_extH, (**the_data_H).ext_cnt);

 if ((old_ext.ext_strt != curr_ext.ext_strt) ||
 !ext_active)  /* we left the current extent */
 {
 if (curr_ext.src_doc==(**the_data_H).doc_ID)      
 /* we were the source */
 {
 iac_code = ext_write(TextH,
  old_ext.ext_strt,
  (old_ext.ext_end - old_ext.ext_strt),
  (**the_data_H).doc_ID,
  old_ext.hat_check,
  &the_ed);
 ext_recs[old_ext_no].ed_level = the_ed;
 /* update old extent just written */
 }
 }
 }
 else /* see if we’ve moved into one! */
 {
 ext_active = chk_extent(TextH, the_extH, (**the_data_H).ext_cnt);
 /* Since we weren’t in an extent, nothing to write */
 }
}

/**
 * Routine: ext_remove
 *
 * This routine severs a link in the drivers dependency table. It assumes
 the driver will take care of removing any remaining data it sent. */

# define __SEG__ Main
void  ext_remove(w_Ptr, extP)
 WindowPtrw_Ptr;
 extentPextP;    /* extent to remove */
{
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
short   iac_code;/* result from IAC call */

 the_data_H = (win_dataH) GetWRefCon (w_Ptr);
 iac_code = iac_remove_dependency((**the_data_H).doc_ID,
  (**the_data_H).the_slot, extP->hat_check);
}

/**
 * Routine: poll_iac
 *
 * This routine checks with the IAC driver to see if there’s anything
 to do. It checks at 1 second intervals to avoid excessive loading on 
 system capacity. In order to simplify management of the extent ranges, 
extents are stored in ascending order by starting position. This means 
that updating an extent involves adjusting the start/end of all SUCCEEDING 
extents by the amount the updated extent changes, but no preceeding extents 
need be touched. Storing the extents in random order is definitely a 
poor idea. */

# define __SEG__ Main
void poll_iac(w_Ptr)
 WindowPtrw_Ptr;
{
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
exTable ext_recs;/* for walking extents to read */
TEHandleTextH;   /* The TextEdit handle */
short   slot_ID, vers, the_ed;/* for IAC */
long    fmt_code;/* actual data format from IAC */
long   fmt_pref[3]; /* format prefs, descending desirability */
long    old_st, old_end;  /* TE selection before poll */
long    my_docID;/* so I don’t read my own extents! */
short delta;/* change in extent size */
short i,j;/* traditional loop counters */
Handle  ext_data;/* Handle to data block */

short iac_code = noErr; /* result from IAC call */
short waiting = 0; /* for IAC */
short doc_count = 0; /* how many are open? */

extern  longlast_poll;    /* when we last polled IAC */

 if (TickCount() > (last_poll+POLL_INT))     /* min interval */
 {
 /* get relevent handles, etc */
 the_data_H = (win_dataH) GetWRefCon (w_Ptr);
 slot_ID = (**the_data_H).the_slot;
 the_extH =  (**the_data_H).the_extents;
 TextH = (**the_data_H).wind_TEH;
 my_docID =  (**the_data_H).doc_ID;
 
 iac_code = iac_status(slot_ID, &vers, &doc_count, &waiting);
 
 if (waiting>0)  /* get data for each and update extents */
 {
 old_st =   (**TextH).selStart;  /* user’s selection */
 old_end =  (**TextH).selEnd;
 ext_recs = *the_extH;    /* point to 1st extent */
 for (i=0; i<(**the_data_H).ext_cnt; i++)
 {
 if (my_docID != ext_recs[i].src_doc) /*not my own! */
 {
 the_ed = ext_recs[i].ed_level + 1; /* successor to last level read */
 iac_code = ext_read(w_Ptr, &the_ed, i);
 
 if (iac_code==noErr)
 {
 if ((waiting -= 1) == 0)
 {
 break; /* stop if no extents left to do */
 }
 }
 }
 } /* for */
 
 TESetSelect (old_st, old_end, TextH); /* restore */
 }
 
 last_poll = TickCount(); /* update timer */
 }
}

/**
 * Routine: edit_init
 *
 * This handles all the grunt initialization. It returns NULL if everything 
went OK, non-NULL if unable to open the IAC driver or we aren’t operating 
under MultiFinder. */

# define __SEG__ init
short edit_init()
{
short   i, k, menu_limit;
Str255  itemString;

short   result = 0;/* default optimism */

extern  MenuHandle MyMenus[];
extern  CursHandle ibeamHdl;
extern  longlast_poll;

 InitGraf(&qd.thePort);
 InitFonts();
 FlushEvents(everyEvent, 0);
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs(nil);
 InitCursor();
 ibeamHdl = GetCursor(iBeamCursor);
 HNoPurge((Handle)ibeamHdl);/* ensure we keep it */

 result = iac_open();/* try to get IAC driver */
 if (result != noErr)/* couldn’t get it! */
 {
 NumToString ((long) result, &itemString);
 if (result==EARLY_SYS)
 {
 ParamText (“MultiFinder”,””,&itemString,””);
 }
 else
 {
 ParamText (“the IAC driver”,””,&itemString,””);
 }
 }
 else   /* got it! */
 {
 ext_active = false; /* no “current extent” yet */
 curr_ext.hat_check = 0;
 curr_ext.ed_level =  0;
 curr_ext.ext_strt =  0;
 curr_ext.ext_end =   0;

 /* handle menu init’g (don’t add link-display menu) */
 menu_limit = menuCount-1;
 for (i=appleMenu,k=appleID; i<menu_limit; i++,k++)
 {
 MyMenus[i] = GetMenu(k);
 }
 AddResMenu(MyMenus[appleMenu], (ResType) ‘DRVR’);
 AddResMenu(MyMenus[fontMenu], (ResType) ‘FONT’);
 for (i=0; i<menu_limit ;++i )
 {
 InsertMenu(MyMenus[i],0);
 }

 /* set textstyling defaults */
 GetFNum (“Geneva”, &the_fNum);
 the_size = 10;
 CheckItem (MyMenus[sizeMenu], 2, true);
 /* scan font menu to check default font */
 for (i=1; i<=CountMItems (MyMenus[fontMenu]); i++)
 {
 GetItem (MyMenus[fontMenu], i, &itemString);
 GetFNum (&itemString, &k);
 if (k==geneva)
 {
 CheckItem (MyMenus[fontMenu], i, true);
 break;
 }
 }
 DrawMenuBar();  /* now user can see menu bar */
 last_poll = TickCount(); /* so we can poll @ 1-sec intervals */
 }

 return(result);
}

/**
 * Routine: menu_tree
 *
 * This is the standard menu-processing tree.
 */

# define __SEG__ Main
void  menu_tree(menu_sel)
 long menu_sel;  /* menu/item selected */
{
short   the_menu, the_item;
GrafPtr savePort;
char    daName[256];
Str255  itemString;/* font selection */
short   i;
win_dataH the_data_H;/* data associated with a window */
extentP extP;  /* ptr to an extent to zap on quitting */
TEHandleTextH;   /* The TextEdit handle */

extern shortthe_fNum, the_size;    /* current attributes */
extern Stylethe_style;
extern MenuHandleMyMenus[];
extern Boolean   DoneFlag;

extern void about_box();
extern shortopen_doc(), create_doc(), close_doc(),             
 save_doc();
extern void do_clear(), do_copy(), do_cut(), do_paste();
extern void do_hotCopy(), do_hotPaste(), killLink_box(),       
 ext_remove();
extern void link_display(), linkInfo_box();

 the_item = LOWORD(menu_sel);
 the_menu = HIWORD(menu_sel); /* This is the resource ID */

 if (myWindow)
 {
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 TextH = (**the_data_H).wind_TEH;
 }

 switch (the_menu)
 {
 case appleID:
 if (the_item == aboutMeCommand) 
 {
 about_box();
 }
 else
 {
 GetItem(MyMenus[appleMenu], the_item, daName);
 GetPort(&savePort);
 (void) OpenDeskAcc(daName);
 SetPort(savePort);
 }
 break;

 case fileID:
 switch (the_item)
 {
 case newCommand:
 (void) create_doc();
 break;

 case openCommand:
 (void) open_doc();
 break;

 case closeCommand:
 (void) close_doc();
 break;

 case saveCommand:
 (void) save_doc();
 break;

 case quitCommand:
 DoneFlag = true;/* I want out! */
 
 for (i=0; i<(**the_data_H).ext_cnt; i++)
 {
 extP = (*(**the_data_H).the_extents) +
    (i * sizeof(extent));
 ext_remove(myWindow, extP);
 }
 break;

 default:
 break;
 }
 break; /* fileID */

 case editID:
 switch (the_item)
 {
 case undoCommand: /* not implemented (if ever!) */
 break;

 case cutCommand:
 do_cut();
 break;

 case copyCommand:
 do_copy();
 break;

 case pasteCommand:
 do_paste();
 break;

 case clearCommand:
 do_clear();
 break;

 case hotCopyCommand:
 do_hotCopy();
 break;

 case hotPasteCommand:
 do_hotPaste();
 break;

 case zapLinkCommand:
 killLink_box();
 break;
 
 default:
 break;
 }
 break; /* editID */

 case optionsID:
 switch (the_item)
 {
 case showLinksCmd:
 break;

 case showLinkInfoCmd:
 linkInfo_box();
 break;

 default:
 break;
 }
 break; /* optionsID */

 case fontID:
 for (i=1; i<=CountMItems(MyMenus[fontMenu]); i++)
 {
 CheckItem (MyMenus[fontMenu], i, false);
 }
 CheckItem (MyMenus[fontMenu], the_item, true);
 GetItem (MyMenus[fontMenu], the_item, &itemString);
 GetFNum (&itemString, &the_fNum);
 for (i=0; i<8; i++) /* set real sizes */
 {
 if (RealFont (the_fNum,f_sizes[i]))
 {
 SetItemStyle (MyMenus[sizeMenu], i+1, outline);
 }
 else
 {
 SetItemStyle (MyMenus[sizeMenu], i+1, normal);
 }
 }
 if (myWindow) /* force a refresh */
 {
 SetPort (myWindow);
 TextFont (the_fNum);
 (**TextH).txFont = the_fNum;
 TECalText (TextH);
 InvalRect (&myWindow->portRect);
 }
 break;

 case sizeID:
 if (the_item<9) /* setting new size */
 {
 for (i=1; i<9; i++) /* remove all checkmarks */
 {
 CheckItem (MyMenus[sizeMenu], i, false);
 }
 the_size = f_sizes[the_item-1];
 CheckItem (MyMenus[sizeMenu], the_item, true);
 }
 else   /* setting style */
 {
 if (the_style & f_styles[the_item-10])/*already on?*/
 {
 the_style &= (~f_styles[the_item-10]);/*turn off*/
 CheckItem (MyMenus[sizeMenu], the_item, false);
 }
 else
 {
 the_style |= f_styles[the_item-10];
 CheckItem (MyMenus[sizeMenu], the_item, true);
 }
 }
 if (myWindow) /* force a refresh */
 {
 SetPort (myWindow);
 TextFace (the_style);
 TextSize (the_size);
 (**TextH).txFace = the_style;
 (**TextH).txSize = the_size;
 TECalText (TextH);
 InvalRect (&myWindow->portRect);
 }
 break;

 case linkDisplayID:
 link_display(the_item - 1);/* zero-based extent subscripts */
 break;

 default:
 break;
 } /* end switch(the_menu) */

 HiliteMenu(0);

 return;
}

/**
 * Routine: add_display_cmd()
 *
 * This is the routine that is called whenever a link is completed.
 It adds a menu item to the “Links” menu so the user can highlight 
 any visible links in a distinctive fashion. */

# define __SEG__ Main
void  add_display_cmd(which, total)
 short  which;   /* position in extent array */
 short  total;   /* total # of extents */
{
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
exTable ext_recs;
Str255  num_str, cmd_str; /* for building menu item */

 the_data_H = (win_dataH) GetWRefCon (myWindow);
 the_extH =  (**the_data_H).the_extents;
 ext_recs =  *the_extH;

 if ((**the_data_H).ext_cnt)
 {
 if (!link_menu) /* need to create link menu */
 {
 MyMenus[linkdDispMenu] = GetMenu(linkDisplayID);
 InsertMenu(MyMenus[linkdDispMenu],0);
 link_menu = true;
 }

 NumToString ((long) total, &num_str);
 if (ext_recs[which].src_doc == (**the_data_H).doc_ID)
 {
 strcpy(&cmd_str, “Source extent “);
 }
 else
 {
 strcpy(&cmd_str, “Target extent “);
 }
 (void) strcat(&cmd_str, &num_str);/* build menu item */
 AppendMenu (MyMenus[linkdDispMenu], &cmd_str);

 DrawMenuBar();  /* now user can see menu bar again */
 }
}

# define __SEG__ Main
void  link_display(ext_no)
 short  ext_no;  /* which extent to highlight */
{
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
exTable ext_recs;/* ptr to array of extents */
short   old_st, old_end;  /* previous selection range */
TEHandleTextH;   /* The TextEdit handle */

extern void SourceHigh(); /* assembler highlighting routine */

 the_data_H = (win_dataH) GetWRefCon (myWindow);
 the_extH =  (**the_data_H).the_extents;
 TextH = (**the_data_H).wind_TEH;
 ext_recs =  *the_extH;

 old_st =  (**TextH).selStart;
 old_end = (**TextH).selEnd;

 /* put a routine address into teHiHook for new highlighting routine 
*/
 (**TextH).highHook = &SourceHigh();
 TESetSelect ((long) ext_recs[ext_no].ext_strt,
  (long) ext_recs[ext_no].ext_end,TextH);

 while (!Button()) ; /* wait for mouse-down */
 while (Button()) ;/* and following mouse-up */

 /* clear teHiHook to restore normal highlighting */
 TESetSelect ((long) old_st, (long) old_end, TextH);           
 /* unhighligh extent */
 (**TextH).highHook = nil;
}


{7}
Listing:  Editor.r

/* resource file for the test editor */ 

#include “Types.r”

/* -- The Menus -- */

resource ‘MENU’ (128, “Apple”, preload) {
 128,
 textMenuProc,
 0x7FFFFFFD,/* Disable item #2 */
 enabled,
 apple,
 {
 “About Sample ”,
 noicon, nokey, nomark, plain;
 “-”,
 noicon, nokey, nomark, plain
 }
};

resource ‘MENU’ (129, “File”, preload) {
 129,
 textMenuProc,
 0x2F,
 enabled,
 “File”,
 {
 “New”,
 noicon, “N”, nomark, plain;
 “Open”,
 noicon, “O”, nomark, plain;
 “Close”,
 noicon, noKey, nomark, plain;
 “Save”,
 noicon, “S”, nomark, plain;
 “-”,
 noicon, nokey, nomark, plain;
 “Quit”,
 noicon, “Q”, nomark, plain
 }
};

resource ‘MENU’ (130, “Edit”, preload) {
 130, textMenuProc,
 0x7FFFFFBD,/* Disable items #1 & #2 */
 enabled, “Edit”,
  {
 “Undo”,
 noicon, “Z”, nomark, plain;
 “-”,
 noicon, nokey, nomark, plain;
 “Cut”,
 noicon, “X”, nomark, plain;
 “Copy”,
 noicon, “C”, nomark, plain;
 “Paste”,
 noicon, “V”, nomark, plain;
 “Clear”,
 noicon, nokey, nomark, plain;
 “-”,
 noicon, nokey, nomark, plain;
 “Hot Copy”,
 noicon, nokey, nomark, plain;
 “Hot Paste”,
 noicon, nokey, nomark, plain;
 “Remove Link”,
 noicon, nokey, nomark, plain
 }
};

resource ‘MENU’ (131,”OptionsMenu”,Preload) {
131,    /* ID */
textMenuProc,  /* menu def proc ID */
0B11,   /* item flags */
enabled,/* menu enable */
“Options”,
 { “Show links”,
   noIcon,noKey,noMark,plain;
 “Show link info”,
   noIcon,noKey,noMark,plain
 }
};

resource ‘MENU’ (132,”FontMenu”,Preload) {
132,    /* ID */
textMenuProc,  /* menu def proc ID */
allEnabled, /* item flags */
enabled,/* menu enable */
“Font”,
{ 
}
};

resource ‘MENU’ (133,”Sizes”,Preload) {
133,    /* ID */
textMenuProc,  /* menu def proc ID */
0B11111011111111,/* item flags */
enabled,/* menu enable */
“Size”,
 { “9”,
   noIcon,noKey,noMark,plain;
   “10”,
   noIcon,noKey,noMark,plain;
   “12”,
   noIcon,noKey,noMark,plain;
   “14”,
   noIcon,noKey,noMark,plain;
   “16”,
   noIcon,noKey,noMark,plain;
   “20”,
   noIcon,noKey,noMark,plain;
   “24”,
   noIcon,noKey,noMark,plain;
   “28”,
   noIcon,noKey,noMark,plain;
   “-”,
   noIcon,noKey,noMark,plain;
   “Bold”,
   noIcon,”B”,noMark,plain;
   “Italic”,
   noIcon,”I”,noMark,plain;
   “Underlined”,
   noIcon,”U”,noMark,plain;
   “Outline”,
   noIcon,noKey,noMark,plain;
   “Shadow”,
   noIcon,noKey,noMark,plain
 }
};

resource ‘MENU’ (134,”Links”,Preload) {
134,    /* ID */
textMenuProc,  /* menu def proc ID */
0xFFFFFFFF, /* item flags */
enabled,/* menu enable */
“Links”,
{ 
}
};

/* -- The Windows -- */

resource ‘WIND’ (128,”a Window”) {
 {64, 60, 314, 460},
 documentProc,
 visible,
 noGoAway,
 0x0,
 “Untitled”
};

/* -- The Alerts/Dialogs (with DITLs) -- */

/* 128 is the about box */
/* 129 is the link info box */

/* 256 is “No IAC” error box */
/* 257 is the IAC error alert */
/* 258 is the “Not in any extents” alert */
/* 259 is the “Sure you want to kill this extent?” Alert */
/* 260 is the “Save changes?” alert */

resource ‘DLOG’ (128,Purgeable) {
 {61, 80, 301, 420},
 altDBoxProc,
 visible,
 noGoAway,
 0x0,
 128,
 “x”
};

resource ‘DITL’ (128,Purgeable) {
 { 
 {200, 110, 225, 230},    /* [1] */
 Button {enabled, “Wonderful!”},
 {15, 65, 31, 275},/* [2] */
 StaticText {disabled,”The Great IAC Sample Program!”},
 {40, 20, 56, 320},/* [3] */
 StaticText {disabled,”This copy: ^0"},
 {60, 20, 124, 320}, /* [4] */
 StaticText {disabled,”This sample is designed to show off the “
 “var- ious capabilities of the  SAWS Inter- Application “
 “Communications Driver. It may be used freely. Send questions, etc”
 “, to: “},
 {124, 119, 140, 219},    /* [5] */
 StaticText {disabled,”Frank Alviani”},
 {140, 120, 188, 220},    /* [6] */
 StaticText {disabled,”P.O. Box 8744 Waukegan Ill 60079"}
 }
};


resource ‘DLOG’ (129,”link info”,Purgeable) {
 {80, 86, 300, 426},
 altDBoxProc,
 visible,
 noGoAway,
 0x0,
 129,
 “”
};

resource ‘DITL’ (129,Purgeable) {
 { {185, 130, 210, 210},  /* [1] */
 Button {enabled,”OK!”},
 {10, 20, 26, 320},/* [2] */
 StaticText {disabled,”Information About Current Registered Links”},
 {35, 300, 170, 316},/* [3] */
 UserItem {enabled},
 {35, 20, 170, 300}, /* [4] */
 UserItem {disabled}
 }
};

/* Scroll bar for link-info dlog */
resource ‘CNTL’ (129,Purgeable) {
  {35,300,170,316},
  0,  /* value */
  visible,
  0,  /* max */
  0,  /* min */
  scrollBarProc, /* type */
  0,  /* refcon */
  “x”
};


resource ‘ALRT’ (256,Purgeable) {
 {100, 100, 220, 400},
 256,
 { OK, visible, sound1, /* [1] */
 OK, visible, sound1,/* [2] */
 OK, visible, sound1,/* [3] */
 OK, visible, sound1 /* [4] */
 }
};

resource ‘DITL’ (256,Purgeable) {
 { {90, 200, 110, 280}, /* [1] */
 Button {enabled, “Rats!” },
 {20, 72, 84, 280},/* [2] */
 StaticText {disabled,
 “Unfortunately, we cannot find ^0. “
 “Without it, there is no point in going on. Farewell...”};
 {94,20,110,150},/* [3] */
 StaticText {disabled, “ID=^2”}
 }
};

resource ‘ALRT’ (257,Purgeable) {
 {100, 100, 240, 400},
 257,
 { OK, visible, sound1, /* [1] */
 OK, visible, sound1,/* [2] */
 OK, visible, sound1,/* [3] */
 OK, visible, sound1 /* [4] */
 }
};

resource ‘DITL’ (257,Purgeable) {
 { {105, 110, 130, 190},  /* [1] */
 Button {enabled,”Rats!”},
 {10, 67, 90, 280},/* [2] */
 StaticText {disabled,”IAC Error ^0 was encountered during the “
 “^1 operation, which was not completed. Sorry!”}
 }
};

resource ‘ALRT’ (258,Purgeable) {
 {100, 100, 200, 400},
 258,
 { OK, visible, sound1, /* [1] */
 OK, visible, sound1,/* [2] */
 OK, visible, sound1,/* [3] */
 OK, visible, sound1 /* [4] */
 }
};

resource ‘DITL’ (258,Purgeable) {
 { {70, 110, 95, 190},    /* [1] */
 Button {enabled,”Rats!”},
 {10, 62, 58, 290},/* [2] */
 StaticText {disabled,”Sorry! You are not in any extent.”
 “Therefore no link can be severed.”}
 }
};

resource ‘ALRT’ (259,Purgeable) {
 {100, 100, 200, 400},
 259,
 { OK, visible, sound1, /* [1] */
 OK, visible, sound1,/* [2] */
 OK, visible, sound1,/* [3] */
 OK, visible, sound1 /* [4] */
 }
};

resource ‘DITL’ (259,Purgeable) {
 { {65, 110, 90, 190},    /* [1] */
 Button {enabled,”Rats!”},
 {10, 62, 42, 290},/* [2] */
 StaticText {disabled,”Are you SURE you want to  remove this “
 “link? This cannot be undone.”}
 }
};

resource ‘ALRT’ (260, Purgeable) {
 {100, 100, 250, 400},
 260,
 { OK, visible, sound1, /* [1] */
 OK, visible, sound1,/* [2] */
 OK, visible, sound1,/* [3] */
 OK, visible, sound1 /* [4] */
 }
};

resource ‘DITL’ (260, Purgeable) {
 { {80, 15, 105, 95},/* [1] */
 Button {enabled,”Save”},
 {115, 15, 140, 95}, /* [2] */
 Button {enabled,”Cancel”},
 {116, 205, 141, 285},  /* [3] */
 Button {enabled,”No”},
 {15, 75, 63, 285},/* [4] */
 StaticText {disabled,”Do you want to save the changes made to ^0?”}
 }
};

resource ‘ALRT’ (261,Purgeable) {
 {100, 100, 200, 400},
 261,
 { OK, visible, sound1, /* [1] */
 OK, visible, sound1,/* [2] */
 OK, visible, sound1,/* [3] */
 OK, visible, sound1 /* [4] */
 }
};

resource ‘DITL’ (261,Purgeable) {
 { {70, 110, 95, 190},    /* [1] */
 Button {enabled,”OK”},
 {10, 62, 58, 290},/* [2] */
 StaticText {disabled,”Doc_ID = ^0 hatCheck = ^1"}
 }
};

/* -- Miscellaneous Resources -- */

resource ‘SIZE’ (-1) {
 dontSaveScreen,
 acceptSuspendResumeEvents,
 disableOptionSwitch,
 canBackground,
 multiFinderAware,
 98304, /* preferrred size = 128K - 32k */
 98304  /* min size = 128K - 32k */
};

type ‘IAC1’ as ‘STR ‘;

resource ‘IAC1’ (0) {
 “Sample IAC Application - Version 1.0ß1”
};

resource ‘ICN#’ (128, Purgeable) {
 { /* array: 2 elements */
 /* [1] */
 $”0001 0400 0002 8A00 0004 5100 0008 2080"
 $”0010 1140 0022 08A0 0044 BFD0 0089 C288"
 $”0101 C104 02A4 9082 054C 2041 080E 4020"
 $”113C 8010 2249 0008 44A0 3F04 8124 4082"
 $”4248 8041 2093 3022 1123 C814 084E 7F8F”
 $”0412 B007 0221 8007 0100 A007 0080 6007"
 $”0040 1FE7 0020 021F 0010 0417 0008 0820"
 $”0004 1040 0002 2880 0001 4500 0000 8200",
 /* [2] */
 $”0001 0400 0003 8E00 0007 DF00 000F FF80"
 $”001F FFC0 003F FFE0 007F FFF0 00FF FFF8"
 $”01FF FFFC 03FF FFFE 07FF FFFF 0FFF FFFF”
 $”1FFF FFFF 3FFF FFFF 7FFF FFFF FFFF FFFF”
 $”7FFF FFFF 3FFF FFFF 1FFF FFFF 0FFF FFFF”
 $”07FF FFFF 03FF FFFF 01FF FFFF 00FF FFFF”
 $”007F FFFF 003F FFFF 001F FFF7 000F FFE0"
 $”0007 FFC0 0003 EF80 0001 C700 0000 8200"
 }
};

resource ‘ICN#’ (129, purgeable) {
 { /* array: 2 elements */
 /* [1] */
 $”0FFF FC00 0800 0600 0800 05C0 0800 04A0"
 $”08F8 0450 0800 0428 081F E7F4 0800 0012"
 $”081F E01F 0800 0011 0803 FC11 0800 001D”
 $”081F 0011 0800 0011 081F FE11 0800 001D”
 $”08FF E011 0800 0011 083C 0019 0800 0011"
 $”080F C011 0800 001D 080F F811 0800 0011"
 $”08FC 0019 0800 0011 083F E011 0800 001D”
 $”0807 FE11 0800 0011 0800 0011 0FFF FFF1",
 /* [2] */
 $”0FFF FC00 0FFF FE00 0FFF FFC0 0FFF FFE0"
 $”0FFF FFF0 0FFF FFF8 0FFF FFFC 0FFF FFFE”
 $”0FFF FFFF 0FFF FFFF 0FFF FFFF 0FFF FFFF”
 $”0FFF FFFF 0FFF FFFF 0FFF FFFF 0FFF FFFF”
 $”0FFF FFFF 0FFF FFFF 0FFF FFFF 0FFF FFFF”
 $”0FFF FFFF 0FFF FFFF 0FFF FFFF 0FFF FFFF”
 $”0FFF FFFF 0FFF FFFF 0FFF FFFF 0FFF FFFF”
 $”0FFF FFFF 0FFF FFFF 0FFF FFFF 0FFF FFFF”
 }
};

resource ‘BNDL’ (128) {
 ‘IAC1’,
 0,
 {
 ‘ICN#’,
 {
 0, 128;
 1, 129
 },
 ‘FREF’,
 {
 0, 128;
 1, 129
 }
 }
};

resource ‘FREF’ (128, purgeable) {
 ‘APPL’,
 0,
 “”
};

resource ‘FREF’ (129, purgeable) {
 ‘TEXT’,
 1,
 “”
};

include $$Shell(“hlxEtc”) “editor.code”;           /* make sure we get 
code segments in! */
{8}
Listing:  Boxes.c

/****
 *
 * These are the routine required to handle the major dialog 
 *boxes.
 ***/

# include <types.h>
# include <memory.h>
# include <quickdraw.h>
# include <toolutils.h>
# include <windows.h>
# include <controls.h>
# include <dialogs.h>
# include <textedit.h>
# include <string.h>
# include <resources.h>
# include <fonts.h>
# include <menus.h>
# include <Events.h>

# include <iac.h>
# define PUBLIC extern
# include <Editor.h>

/**
 * Routine: about_box
 *
 * This handles the about box; the current name of this app is displayed 
to make working with multiple copies easier. No other fancy effects this 
time... */

# define __SEG__ Main
void  about_box()
{
DialogPtr d_Ptr;
short   item_hit;
PtrcurApNmP;/* LOW MEMORY GLOBAL! */

 curApNmP = (Ptr) 0x910;
 PARAMTEXT (curApNmP,””,””,””);  /* Pascal interface ver. */
 d_Ptr = GetNewDialog (ABOUT_DLOG, nil, (WindowPtr) -1);
 ModalDialog (nil, &item_hit);
 DisposDialog (d_Ptr);
}

/**
 * Routine: linkInfo_box()
 *
 * This routine displays a dialog box with information about each link 
in the current document. */

# defineSCROLL_BAR_ITEM 3
# defineLIST_BOX 4

# define __SEG__ Main
void   linkInfo_box()

{
short   iac_code;/* result from IAC call */
DialogPtr d_Ptr;
short   i, lines_vis;/* scratch */
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
exTable ext_recs;/* ptr to extent-array */
TEHandleTextH;   /* The TextEdit handle */
ControlHandle  scrollH;   /* Scroll bar handle */
String(16)ext_no;/* holds converted values */
short   i_type;  /* for GetDItem */
Handle  i_hdnl;
Rect    i_box;
char    *msg;
short   e_cnt;   /* count of active extents */
info_tblcensus_info; /* census info for every extent */
Point   mouse_loc;
Boolean b;

short   item_hit = 0;

 /* get necessary handles, etc. set up */
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 the_extH =  (**the_data_H).the_extents;

 iac_code = iac_census(&e_cnt, &census_info); /* que pasa, driver? */
 if (e_cnt==0)
 {
 return;
 }

 /* set up text area */
 d_Ptr = GetNewDialog (ABOUT_LINKS, nil, (WindowPtr) -1);
 SetPort ((GrafPtr) d_Ptr);
 TextFont (monaco);/* mono-spaced for easy layout */
 TextSize (9);
 GetDItem (d_Ptr, LIST_BOX, &i_type, &i_hdnl, &i_box);   /* text-area 
box */
 i_box.right += 1; /* overlap scroll bar properly */
 FrameRect (&i_box);
 InsetRect (&i_box, 2, 2);/* margin for drawing */
 TextH = TENew (&i_box, &i_box);
 lines_vis = (i_box.bottom - i_box.top) / 12; /* text lines visible */

 /* Highlight button */
 GetDItem (d_Ptr, ok, &i_type, &i_hdnl, &i_box);/* button */
 InsetRect (&i_box, -4, -4);
 PenSize (3, 3);
 FrameRoundRect (&i_box, 16, 16);
 PenNormal();

 /* set up scroll bar */
 scrollH = GetNewControl (ABOUT_LINKS, d_Ptr);
 if (e_cnt > lines_vis) /* enable control */
 {
 SetCtlMax (scrollH, e_cnt);
 }

 /* put in header */
 msg = “Ext# --Doc_ID--  Hat_check  Edition\n\n”;
 TEInsert (msg, (long) strlen(msg), TextH);

 /* do each extent in turn */
 for (i=0; i<e_cnt; i++)
 {
 sprintf(&ext_no, “%4d”, i);
 TEInsert (&ext_no, 4L, TextH);
 sprintf(&ext_no, “ $%8X”,census_info.ext_entry[i].doc_ID);
 TEInsert (&ext_no, 10L, TextH);
 sprintf(&ext_no, “%9d”, census_info.ext_entry[i].hat_check);
 TEInsert (&ext_no, 9L, TextH);
 sprintf(&ext_no, “%9d”,census_info.ext_entry[i].ed_level);
 TEInsert (&ext_no, 9L, TextH);
 TEKey(0x0D, TextH); /* \n to end line */
 }
 TextFont(systemFont);    /* so static text looks right */
 TextSize (12);

 /*
  *The normal modal dialog loop. You can’t do anything except look at 
the range of each extent. */
 while (item_hit != ok)
 {
 ModalDialog (nil, &item_hit);
 switch (item_hit)
 {
 case SCROLL_BAR_ITEM:
 GetMouse (&mouse_loc);
 GlobalToLocal (&mouse_loc);
 /* scrolling code here later */
 break;

 default:
 break;
 }
 }
 DisposeControl (scrollH);/* clean up after ourselves */
 DisposDialog (d_Ptr);
 SetPort(FrontWindow());
}

/**
 * Routine: killLink_box
 *
 * This is the user interface that allows a user to “cut” a hot link. 
 */

# define __SEG__ Main
void  killLink_box()
{
short   item_hit;

extern void kill_extent();

 if (ext_active)
 {
 item_hit = StopAlert (KILL_EXT, nil); /* Are you SURE? */
 if (item_hit == ok)
 {
 kill_extent();
 }
 }
 else
 {
 item_hit = StopAlert (NOT_IN_EXT, nil);     /* not in any extent */
 }
}
{9}
Listing:  Doc_rtns.c

/****
 *
 * These are the routines required to actually carry out the document-handling
 functions. They are invoked from menu_tree() and are arranged in alphabetic
 order to simplify finding them. Each ensures that the menus are properly 
 enabled and disabled.
 */

# include <types.h>
# include <memory.h>
# include <packages.h>
# include <quickdraw.h>
# include <toolutils.h>
# include <windows.h>
# include <dialogs.h>
# include <menus.h>
# include <textedit.h>
# include <string.h>
# include <files.h>
# include <resources.h>

# include <iac.h>
# define PUBLIC extern
# include <Editor.h>

#define noErr     0   /* 0 for success */
#define fnfErr    -43 /* File not found */

/**
 * Routine: close_doc
 *
 * This is the code required to close (and save if necessary) a document. 
*/

# define __SEG__ Main
short close_doc()
{
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
exTable ext_recs;/* ptr to array of extents */
TEHandleTextH;   /* The TextEdit handle */
short   i, item, nm_len;
short   iac_code;/* result from IAC call */
Str255  st0;

extern shortsave_doc();   /* saved updated document */

extern WindowPtr myWindow;/* the text window */

# define no 3  /* button id */

 the_data_H = (win_dataH) GetWRefCon (myWindow);
 the_extH =  (**the_data_H).the_extents;
 TextH = (**the_data_H).wind_TEH;
 ext_recs =  *the_extH;

 /* check about saving, etc. */
 if ((**the_data_H).dirty)
 {
 nm_len = (**the_data_H).doc_file_nm.length;
 if (nm_len)
 {
 ParamText (&(**the_data_H).doc_file_nm, “”, “”, “”);
 }
 else
 {
 ParamText (“Untitled”, “”, “”, “”);
 }

 item = StopAlert (SAVE_CHANGES, nil);
 switch (item)
 {
 case ok:
 save_doc();
 break;

 case cancel:
 return 0;/* don’t delete extents, etc. */
 break;

 case no:
 break;
 }
 }

 /* shut down extents... */
 for (i=0; i<(**the_data_H).ext_cnt; i++)
 {
 iac_code = iac_remove_dependency(ext_recs[i].src_doc,
  (**the_data_H).the_slot,
  ext_recs[i].hat_check);

 if (iac_code != noErr && iac_code != NO_SUCH_DEP)
 {
 NumToString ((long)iac_code, &st0);
 ParamText (&st0,”Remove Dependency”,nil,nil);
 item = StopAlert (IAC_ERR_ALRT, nil);
 }
 }

 /* clean up storage associated with window */
 the_data_H = (win_dataH) GetWRefCon(myWindow);
 DisposHandle((Handle) (**the_data_H).the_extents);
 DisposHandle((Handle) the_data_H);
 CloseWindow (myWindow);

 /* put menu items into proper states */
 EnableItem (MyMenus[fileMenu], newCommand);
 EnableItem (MyMenus[fileMenu], openCommand);
 DisableItem (MyMenus[fileMenu], closeCommand);
 DisableItem (MyMenus[fileMenu], saveCommand);
 DisableItem (MyMenus[editMenu], 0); /* nothing to edit */
 DisableItem (MyMenus[optionsMenu], 0);
 DisableItem (MyMenus[fontMenu], 0);
 DisableItem (MyMenus[sizeMenu], 0);

 DeleteMenu (linkdDispMenu);/* no doc, no menu  */
 DisposeMenu (MyMenus[linkdDispMenu]);
 MyMenus[linkdDispMenu] = nil;
 
 DrawMenuBar;
}

/**
 * Routine: create_doc
 *
 * This is the code for creating a new document. */

# define __SEG__ Main
short create_doc()
{
win_dataH the_data_H;/* data associated with a window */

extern WindowPtr myWindow;/* the text window */
extern void setup_wind(); /* set up window */

 setup_wind();   /* create window & data structures */
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 (**the_data_H).doc_file_nm.length = 0;/* no name yet */

 /* put menu items into proper states */
 DisableItem (MyMenus[fileMenu], newCommand);
 DisableItem (MyMenus[fileMenu], openCommand);
 EnableItem (MyMenus[fileMenu], closeCommand);
 EnableItem (MyMenus[fileMenu], saveCommand);
 EnableItem (MyMenus[editMenu], 0);/* stuff to edit */
 EnableItem (MyMenus[optionsMenu], 0);
 EnableItem (MyMenus[fontMenu], 0);
 EnableItem (MyMenus[sizeMenu], 0);
 DrawMenuBar;
}

/**
 * Routine: open_doc
 *
 * This is the code required to open a document. After opening the doc, 
reading in the text, and reloading the extents, it checks to see if ther 
are any documents open of which it was a target in a previous life, and 
re-establishes those links. After that, it re-starts any links for which 
it was the source. */

# define __SEG__ Main
short open_doc()
{
IOParam the_blk;
Handle  txtH;    /* Handle to text itself */
Handle  extRH;   /* extent-resource handle */
long inttxt_size;/* for TextEdit */
SFReply a_reply; /* for SFGetFile */
Point   where;
SFTypeListthe_types;
OSErr   an_err;
short   res_refNum;/* for resource work */
short   slot_ID, vers, the_ed;/* for IAC */
short   c_ndx, e_ndx, e_cnt;/* census-walking loops */
long    t_doc;   /* temporaries to avoid memory problems */
short   t_slot, t_hatchk;
exTable ext_recs;/* ptr to extent-array */
win_dataH the_data_H;/* data associated with a window */
win_dataH winRH; /* window data from saved file */
TEHandleTextH;   /* The TextEdit handle */
info_tblthe_census;/* info for each dependency */

short   iac_code = noErr; /* result from IAC call */
short   doc_count = 0;    /* how many are open? */
short   ext_so_far = 0;   /* counter for ‘Links’ menu */

extern void setup_wind(); /* set up window */
extern void add_display_cmd();/* update ‘Links’ menu */


 SetPt (&where, 80, 100);
 the_types[0] = (OSType) 0x54455854; /* ‘TEXT’ */
 SFGetFile (&where, “”, nil, 1, &the_types[0], nil, &a_reply.good);
 if (a_reply.good)
 {
 /* an_err = SetVol(nil, &a_reply.vRefNum); /* set default volume */
 an_err = FSOPEN (&a_reply.fName, a_reply.vRefNum, &fRef);

 the_blk.ioCompletion = nil;
 the_blk.ioRefNum = fRef;
 an_err = PBGetEOF(&the_blk.qLink, false);
 txt_size = (long int) the_blk.ioMisc;
 txtH = NewHandle (txt_size);

 HLock(txtH);
 the_blk.ioCompletion = nil;/* set up driver parameter block */
 the_blk.ioRefNum =   fRef;
 the_blk.ioBuffer =   *txtH;
 the_blk.ioReqCount =   txt_size;
 the_blk.ioPosMode =    fsFromStart;
 the_blk.ioPosOffset =  0;
 an_err = PBRead(&the_blk.qLink, false);

 setup_wind();   /* set up window and TE record */
 SETWTITLE (myWindow, &a_reply.fName); /*window title OK*/
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 TextH = (**the_data_H).wind_TEH;
 TESetText (*txtH, txt_size, TextH); /* TE record */
 HUnlock(txtH);
 DisposHandle (txtH);/* no longer needed */

 /* get saved data & attach it to the window */
 res_refNum = OPENRESFILE(&a_reply.fName);
 extRH = GetResource (‘EXTN’, 0);  /* get extents */
 DetachResource (extRH); /* remove from resource manager */
 DisposHandle((Handle) (**the_data_H).the_extents);            
 /* kill old table */
 (**the_data_H).the_extents = extRH;

 winRH = (win_dataH) GetResource(‘EXTN’, 1); /* get saved window data 
*/
 (**the_data_H).doc_ID =   (**winRH).doc_ID;
 (**the_data_H).ext_cnt =  (**winRH).ext_cnt;
 DisposHandle((Handle) winRH);/* no longer needed */

 the_blk.ioCompletion = nil;
 the_blk.ioRefNum =   fRef;
 an_err = PBClose(&the_blk.qLink, false);    /* close file */

 /*We get a census of existing dependencies, and check them
 against our extent data. For each match, we make that the
 “available dependency” and re-establish the link. */

 iac_code = iac_census(&doc_count, &the_census);
 if (iac_code == noErr)
 {
 e_cnt = (**the_data_H).ext_cnt;
 HLock(extRH); /* IAC calls may move memory */
 
 for (c_ndx=0; c_ndx<doc_count; c_ndx++)/* walk census */
 {
 ext_recs = *(extentH) extRH; /* base of table */
 for (e_ndx=0; e_ndx<e_cnt; e_ndx++)/* walk extents */
 {
 if ((the_census.ext_entry[c_ndx].doc_ID ==
 ext_recs[e_ndx].src_doc) &&
 (the_census.ext_entry[c_ndx].hat_check == 
 ext_recs[e_ndx].hat_check))
 {
 /* make this dependency “available” for completion */
 iac_code = iac_available_dependency( ext_recs[e_ndx].src_doc,ext_recs[e_ndx].hat_check);
 
 /* complete “available dependency” */
 t_doc = 0; /* temps across IAC calls */
 t_hatchk = 0;   /* already made extent avail */
 t_slot = (**the_data_H).the_slot;
 iac_code = iac_complete_dependency(&t_doc,
  &t_slot, &t_hatchk);
 (**the_data_H).the_slot = t_slot;/* could change */

 /* read initial copy of data from restarted link */
 the_ed = 0;
 iac_code = ext_read(myWindow, &the_ed, e_ndx);

 ext_so_far += 1;
 add_display_cmd(e_ndx, ext_so_far); 
 /* add link to menu */
 }
 } /* for */
 } /* got census */

 if (e_cnt) /* restart links for which we are source */
 {
 ext_recs = *(extentH) extRH;
 t_doc =  (**the_data_H).doc_ID;
 t_slot = (**the_data_H).the_slot;
 for (e_ndx=0; e_ndx<e_cnt; e_ndx++)
 {
 if (ext_recs[e_ndx].src_doc == (**the_data_H).doc_ID)
 {
 t_hatchk = ext_recs[e_ndx].hat_check;
 the_ed = 0;
 iac_code = iac_add_dependency(&t_doc,
 &t_slot,&t_hatchk,&the_ed);
 (**the_data_H).the_slot = t_slot;/* could change */

 /* write data to restarted dependency */
 iac_code = ext_write(TextH,
  ext_recs[e_ndx].ext_strt,
  /* ext_recs[e_ndx].ext_strt - ext_recs[e_ndx].ext_end, */
  1,    /* temporary splint */
  t_doc,
  t_hatchk,
  &the_ed);
 
 ext_recs[e_ndx].ed_level = the_ed;
 ext_so_far += 1;
 add_display_cmd(e_ndx, ext_so_far); 
 /* add link to menu */
 }
 }
 }
 
 HUnlock(extRH); /* unclutter memory */
 }

 /* put menu items into proper states */
 DisableItem (MyMenus[fileMenu], newCommand);
 DisableItem (MyMenus[fileMenu], openCommand);
 EnableItem (MyMenus[fileMenu], closeCommand);
 DisableItem (MyMenus[fileMenu], saveCommand);
 EnableItem (MyMenus[editMenu], 0);/* stuff to edit */
 EnableItem (MyMenus[optionsMenu], 0);
 EnableItem (MyMenus[fontMenu], 0);
 EnableItem (MyMenus[sizeMenu], 0);
 DrawMenuBar;
 }
}

/**
 * Routine: save_doc()
 *
 * This is the code required to save a document.
 */

# define __SEG__ Main
short save_doc()

{
win_dataH the_data_H;/* data associated with a window */
TEHandleTextH;   /* The TextEdit handle */
IOParam the_blk;
Handle  txtH;    /* Handle to text itself */
Handle  tmp_H;   /* temporary for resource work */
short   nm_len;
short   refNum, res_refNum;
Str255  prompt,orig_Name;
SFReply aReply;
Point   where;
OSErr   anErr;
long inttxt_size;

 the_data_H = (win_dataH) GetWRefCon (myWindow);
 TextH = (**the_data_H).wind_TEH;
 nm_len =  (**the_data_H).doc_file_nm.length;

 if (nm_len==0)  /* invoke SFPutFile for file name */
 {
 where.h = 80;
 where.v = 80;
 SFPutFile (&where, “Name your new document”, “IAC Doc”, nil, &aReply);
 if (aReply.good)
 {
 strcpy(&(**the_data_H).doc_file_nm, &aReply.fName);
 /* CHECK! */
 }
 else
 {
 return 0;
 }
 }

 anErr = FSOPEN(&(**the_data_H).doc_file_nm, aReply.vRefNum, &refNum);
 if (anErr) /* may not exist yet.. */
 {
 if (anErr==fnfErr)
 {
 anErr = CREATE(&(**the_data_H).doc_file_nm, aReply.vRefNum, ‘IAC1’ , 
‘TEXT’);
 anErr = FSOPEN(&(**the_data_H).doc_file_nm, aReply.vRefNum, &refNum);
 (void) CREATERESFILE(&(**the_data_H).doc_file_nm);
 }
 else
 {
 return (anErr);
 }
 }
 res_refNum = OPENRESFILE(&(**the_data_H).doc_file_nm);
 
 /* save text itself in data fork */
 txtH = (**TextH).hText;
 txt_size = GetHandleSize (txtH);
 HLock(txtH);
 the_blk.ioCompletion = nil;
 the_blk.ioRefNum =  refNum;
 the_blk.ioBuffer =  *txtH;
 the_blk.ioReqCount =   txt_size;
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset =  0;
 anErr = PBWrite(&the_blk.qLink, false);
 HUnlock(txtH);
 
 /* save extents in ‘EXTN’ resource */
 if (CountResources (‘EXTN’)) /* updating existing copy */
 {
 tmp_H = GetResource (‘EXTN’, 0);  /* read old version */
 RmveResource (tmp_H);    /* kill it */
 tmp_H = GetResource (‘EXTN’, 1);  /* read old version */
 RmveResource (tmp_H);    /* kill it */
 }
 AddResource ((**the_data_H).the_extents, ‘EXTN’, 0, nil);
 AddResource ((Handle) the_data_H, ‘EXTN’, 1, nil);
 UpdateResFile (res_refNum);
 
 /* close up shop till next time */
 the_blk.ioCompletion = nil;
 the_blk.ioRefNum =refNum;
 anErr = PBClose(&the_blk.qLink, false);
 /* close file */

 the_blk.ioCompletion = nil;
 the_blk.ioNamePtr = nil;
 the_blk.ioVRefNum = aReply.vRefNum;
 anErr = PBFlushVol (&the_blk.qLink, false);
 
 /* ensure disk updated */

 SETWTITLE (myWindow, &aReply.fName);
 
 (**the_data_H).dirty = false;
 return (0);
}

/**
 * Routine: setup_wind
 *
 * This allocates the window and creates the TextEdit data structure 
and   auxiliary data record required. */

# define __SEG__ Main
void  setup_wind()
{
Rect    txRect;
extentH temp_extH; /* handle to extents block */
Str255  tStr;

extern  WindowPtrmyWindow;/* the text window */
extern  WindowRecord wRecord;
extern  short    the_fNum, the_size; /* text attributes */
extern  Style    the_style;

 myWindow = GetNewWindow(windowID, &wRecord, (WindowPtr) -1);
 SetPort(myWindow);
 txRect = myWindow->portRect;
 InsetRect(&txRect, 4, 0);
 TextH = TENew(&txRect, &txRect);  /* Not growable, so 
 destRect == viewRect */
 curr_ext_no = -1; /* no ‘current extent’ yet */

 the_data_H = (win_dataH) NewHandle(sizeof(win_data));
 /* space for data */
 SetWRefCon (myWindow, (long int) the_data_H);
 /* keep with window */
 (**the_data_H).the_slot = 0; /* slot_ID for this document */
 (**the_data_H).doc_ID =   0; /* document ID for this doc */
 (**the_data_H).ext_cnt =  0; /* No. of extents with this doc */
 (**the_data_H).wind_TEH = TextH;  /* TE handle for this window */
 (**the_data_H).dirty =   false;
 (**the_data_H).relevent = 0;

 /* empty block of extent records */
 temp_extH = (extentH) NewHandle(sizeof(extent));
 (**temp_extH).hat_check = 0;
 (**temp_extH).ed_level =  0;
 (**temp_extH).ext_strt =  0;
 /* start & end of extent range */
 (**temp_extH).ext_end =    0;
 (**the_data_H).the_extents = temp_extH;

 (**TextH).txFont = the_fNum;
 /* set text attributes */
 (**TextH).txSize = the_size;
 (**TextH).txFace = the_style;
 
}

{10}

Continued in nest frame

Volume Number:4
Issue Number:11
Column Tag:Advanced Mac'ing

IAC Driver Demo in MPW C (code)


Listing: Edit_IAC_rtns.c

# include <types.h>
# include <memory.h>
# include <quickdraw.h>
# include <toolutils.h>
# include <windows.h>
# include <dialogs.h>
# include <menus.h>
# include <textedit.h>
# include <string.h>
# include <files.h>
# include <resources.h>

# include <iac.h>
# define PUBLIC extern
# include <Editor.h>

#define noErr     0
    /* 0 for success */

/**
 * Routine: Set Current Extent
 *
 * This is a local routine to set the fields in the ‘current extent’
 structure in the window data record. It is called by both hotCopy and
 hotPaste routines, since by definitions the extents they define become
 the ‘current extent’. */

# define __SEG__ Main
void  set_curr_ext(the_data_H, which)
 win_dataHthe_data_H;/* data associated with the window */
 short  which;   /* # of extent which is now current */
{
extentH the_extH;/* handle to extent block */
exTable ext_recs;

 the_extH = (**the_data_H).the_extents;
 ext_recs = *the_extH;

 curr_ext.src_doc =  ext_recs[which].src_doc;
 curr_ext.hat_check = ext_recs[which].hat_check;
 curr_ext.ed_level = ext_recs[which].ed_level;
 curr_ext.ext_strt = ext_recs[which].ext_strt;
 curr_ext.ext_end =ext_recs[which].ext_end;
}

/**
 * Routine: do_hotCopy
 *
 * This routine is responsible for creating a new dependency source and
 notifying the IAC driver. It is called primarily by the “source” program. 
 */

# define __SEG__ Main
void  do_hotCopy()

{
win_dataH the_data_H;/* data associated with a window */
TEHandleTextH;   /* The TextEdit handle */
extentH the_extH;/* handle to extent block */
exTable ext_recs;
long    the_doc; /* local copies due to memory mashing */
short   slot_ID, h_check, the_ed;
short   e_cnt;
short   item_hit;/* error processing */
Str255  err_str, str2;

short   iac_err = noErr;
Boolean in_extent = false;

extern  Boolean  chk_extent();/* sets “current extent” if found */
extern  voidadd_display_cmd();/* update ‘Links’ menu */
extern  voidset_curr_ext(); /* update global structure */

extern  Boolean  ext_active;

 the_data_H = (win_dataH) GetWRefCon (myWindow);
 the_extH =  (**the_data_H).the_extents;
 TextH = (**the_data_H).wind_TEH;
 the_doc = (**the_data_H).doc_ID;
 slot_ID = (**the_data_H).the_slot;
 e_cnt = (**the_data_H).ext_cnt;
 h_check = 0;

 if (!chk_extent(TextH, the_extH, (**the_data_H).ext_cnt))
 {
 iac_err = iac_add_dependency(&the_doc, &slot_ID, &h_check, &the_ed);
 
 if (iac_err == noErr)
 {
 SetHandleSize ((Handle)the_extH, sizeof(extent) * (e_cnt+1));
 ext_recs = *the_extH;
 ext_recs[e_cnt].hat_check = h_check;
 ext_recs[e_cnt].ed_level =  the_ed;
 ext_recs[e_cnt].ext_strt =  (**TextH).selStart;
 ext_recs[e_cnt].ext_end =(**TextH).selEnd;
 ext_recs[e_cnt].src_doc =   the_doc;
 set_curr_ext(the_data_H,e_cnt);
 
 curr_ext_no = e_cnt;
 e_cnt += 1;
 (**the_data_H).doc_ID =    the_doc; /* update window */
 (**the_data_H).the_slot = slot_ID;
 (**the_data_H).ext_cnt =  e_cnt;
 (**the_data_H).dirty =   true;    /* so save extents */
 ext_active = true;
 
 iac_err = ext_write(TextH,
 (**TextH).selStart,
 ((**TextH).selEnd - (**TextH).selStart),
 the_doc,
 h_check,
 &the_ed);

 add_display_cmd(curr_ext_no, e_cnt);/* update menu */
 }
 else /* IAC alert here */
 {
 NumToString ((long)iac_err, &err_str);
 ParamText (&err_str, “Hot Copy”, nil, nil);
 item_hit = StopAlert(IAC_ERR_ALRT, nil);
 }
 }
 else /* already-in-extent alert */
 {
 ParamText (“\”already-in-extent\””, “Hot Copy”, nil, nil);
 item_hit = StopAlert(IAC_ERR_ALRT, nil);
 }
}

/**
 * Routine: do_hotPaste
 *
 * This routine is responsible for completing a new dependency and
 notifying the IAC driver. It is called primarily by the “target” program.
 The dependencies are stored in the array in ascending sequence to make 
 updating simpler. See the header for poll_iac() for details. */

# define __SEG__ Main
void  do_hotPaste()

{
win_dataH the_data_H;/* data associated with a window */
TEHandleTextH;   /* The TextEdit handle */
extentH the_extH;/* handle to extent block */
exTable ext_recs;
long    the_doc; /* local copies due to memory mashing */
short   slot_ID, h_check;
short   e_cnt, startt;
short   the_ed;
short   item_hit;/* error processing */
Str255  err_str, str2;

short   i = 0;
short   iac_err = noErr;
Boolean in_extent = false;

extern  Boolean  chk_extent();/* sets “current extent” */
extern  voidadd_display_cmd();/* update ‘Links’ menu */

extern  Boolean  ext_active;

 the_data_H = (win_dataH) GetWRefCon (myWindow);
 the_extH =  (**the_data_H).the_extents;
 TextH = (**the_data_H).wind_TEH;
 slot_ID = (**the_data_H).the_slot;
 e_cnt = (**the_data_H).ext_cnt;
 the_doc = 0;    /* just link to “available” */
 h_check = 0;
 
 if (!chk_extent(TextH, the_extH, e_cnt))
 {
 iac_err = iac_complete_dependency(&the_doc, &slot_ID, &h_check);
 if (iac_err == noErr)  /* save “location” in TERec as extent */
 {
 (**the_data_H).the_slot = slot_ID;/* update window */
 ext_active = true;
 startt = (**TextH).selStart;
 SetHandleSize ((Handle)the_extH, sizeof(extent) * (e_cnt+1));

 /* walk extent table to find entry to insert BEFORE */
 ext_recs = *the_extH;
 while (i<e_cnt)
 {
 if ( (ext_recs[i].ext_strt==0)  /* at end of table */
   || (ext_recs[i].ext_strt>startt) ) /* table entry bigger */
 {
 /* move tail of table to make space */
 BlockMove(&ext_recs[i],
   &ext_recs[i+1],
   (e_cnt - i) * sizeof(extent));
 break;
 }
 else
 {
 i += 1;
 }
 }
 ext_recs[i].hat_check = h_check;/* fill table entry */
 ext_recs[i].ext_strt =  (**TextH).selStart;
 ext_recs[i].ext_end  =  (**TextH).selEnd;
 ext_recs[i].src_doc =   the_doc;
 set_curr_ext(the_data_H,i);

 /* now read data from newly connected link */
 the_ed = 0;
 iac_err = ext_read(myWindow, &the_ed, i);
 
 extent_count += 1;/* global count */
 curr_ext_no = i;
 (**the_data_H).ext_cnt = e_cnt + 1;
 (**the_data_H).dirty =  true;
 add_display_cmd(i, extent_count); /* update menu */
 }
 else
 {
 NumToString ((long)iac_err, &err_str);
 ParamText (&err_str, “Hot Paste”, nil, nil);
 item_hit = StopAlert(IAC_ERR_ALRT, nil);
 }
 }
 else /* already-in-extent alert */
 {
 ParamText (“\”already-in-extent\””, “Hot Paste”, nil, nil);
 item_hit = StopAlert(IAC_ERR_ALRT, nil);
 }
}

{11}
Listing:  Edit_rtns.c
/****
 *
 * These are the routines needed for the actual text-editing functions. 
They are arranged alphabetically for ease of finding them. */
 
# include <types.h>
# include <errors.h>
# include <memory.h>
# include <packages.h>
# include <quickdraw.h>
# include <toolutils.h>
# include <fonts.h>
# include <windows.h>
# include <dialogs.h>
# include <menus.h>
# include <textedit.h>
# include <string.h>
# include <files.h>
# include <resources.h>

# include <iac.h>
# define PUBLIC extern
# include <Editor.h>

 /**
 * Routine: do_clear
 *
 * This is the routine that carries out the “clear” editing function. 
It uses the normal TE routine to manipulate the text. In addition, it 
checks  the source extents to see if any of them are affected by it, 
and if so,it will post the changed extent to the IAC driver for everybody 
else’s use. */

# define __SEG__ Main
void  do_clear()
{
short   this_ed; /* edition (updated) */
short   hit_ext; /* extent affect by this call */
short   i;/* scratch */
short   strt, endd;/* range in TE record */
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
extentP extP;
TEHandleTextH;   /* The TextEdit handle */
Handle  ext_data;/* data for extent */

short   iac_code = noErr; /* result from IAC call */
Boolean in_extent = false;
Boolean ext_killed = false;

extern  Boolean  chk_extent();/* sets “current extent” if found */
extern  voidkill_extent();

 /* get necessary handles, etc. set up */
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 TextH = (**the_data_H).wind_TEH;
 the_extH =  (**the_data_H).the_extents;

 /* Check extents before actually cutting. Remember which one and we’ll 
update the edition after doing the delete... */
 in_extent = chk_extent(TextH,the_extH, (**the_data_H).ext_cnt);

 TEDelete (TextH);
 (**the_data_H).dirty = true;
 /* if needed, write revised extent to IAC driver */
 if (in_extent)  /* this delete affected the extent */


 {
 /* adjust range for extent */

 /* If the entire extent was removed, tell IAC driver */
 if (ext_killed)
 {
 iac_code = iac_remove_dependency((**the_data_H).doc_ID,
   (**the_data_H).the_slot,
   curr_ext.hat_check);
 kill_extent();  /* always removes current extent */
 }
 else /* write to driver */
 {
 this_ed = curr_ext.ed_level;
 /* data setup here */
 iac_code = iac_write_data((**the_data_H).doc_ID, 
   curr_ext.hat_check, 
   &this_ed,
   1,
   ext_data);
 /* now adjust extent */
 }
 }
}

 /**
 * Routine: do_copy
 *
 * This is the routine that carries out the “copy” editing function. 
It uses the normal TE routine to manipulate the text.  */

# define __SEG__ Main
void  do_copy()

{
short   this_ed; /* edition (updated) */
short   i;/* scratch */
short   strt, endd;/* range in TE record */
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* hand to extent block */
extentP extP;
TEHandleTextH;   /* The TextEdit handle */
Handle  ext_data;/* data for extent */

short   iac_code = noErr; /* result from IAC call */
Boolean in_extent = false;

extern  Boolean  chk_extent();/* sets “current extent” */

 /* get necessary handles, etc. set up */
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 TextH = (**the_data_H).wind_TEH;
 the_extH =  (**the_data_H).the_extents;
in_extent =  chk_extent(TextH,the_extH,(**the_data_H).ext_cnt);
 TECopy (TextH);
}

 /**
 * Routine: do_cut
 *
 * This is the routine that carries out the “cut” editing function. It
 uses the normal TE routine to manipulate the text. In addition, it checks
 the source extents to see if any of them are affected by it, and if 
so,it will post the changed extent to the IAC driver for everybody else’s 
use. */

# define __SEG__ Main
void  do_cut()

{
short   this_ed; /* edition (updated) */
short   hit_ext; /* extent affect by this */
short   i;/* scratch */
short   strt, endd;/* range in TE record */
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* hand to extent block */
extentP extP;
TEHandleTextH;   /* The TextEdit handle */
Handle  ext_data;/* data for extent */
OSErr   an_err;

short   iac_code = noErr; /* result from IAC call */
Boolean in_extent = false;
Boolean ext_killed = false;

extern  Boolean  chk_extent();/* sets “current extent”  */
extern  voidkill_extent();

 /* get necessary handles, etc. set up */
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 TextH = (**the_data_H).wind_TEH;
 the_extH =  (**the_data_H).the_extents;

 /* Check extents before actually cutting. Remember which one and we’ll 
update the edition after doing the cut...  */
in_extent = chk_extent(TextH,the_extH,(**the_data_H).ext_cnt);
 TECut (TextH);
 (**the_data_H).dirty = true;

 /* if needed, write revised extent to IAC driver */
 if (in_extent)  /* this cut affected the extent */
 {
 /* adjust range for extent */

 /* If the entire extent was removed, tell IAC driver */
 if (ext_killed)
 {
 iac_code = iac_remove_dependency((**the_data_H).doc_ID,
   (**the_data_H).the_slot,
   curr_ext.hat_check);
 kill_extent();  /* always removes current extent */
 }
 else /* write to driver */
 {
 HLock((Handle) TextH);
 /* data setup here */
 an_err = PtrToHand (&(**TextH).selStart,&ext_data,
 (long) curr_ext.ext_end - curr_ext.ext_strt + 1);
 HUnlock((Handle) TextH);
 this_ed = curr_ext.ed_level;
 iac_code = iac_write_data((**the_data_H).doc_ID, 
   curr_ext.hat_check, 
   &this_ed,1,ext_data);
 } /* end else */
 } /* end in_extent */
}

/**
 * Routine: do_key
 *
 * This is the routine that handles normal typing. It does NOT notify
 the IAC driver after every keystroke. */

# define __SEG__ Main
void  do_key(msg)
 long   msg;/* event record message field */ 

{
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* hand to extent block */
TEHandleTextH;   /* The TextEdit handle */
char    the_ch;
short   e_cnt;

Boolean in_extent = false;

extern  Boolean  chk_extent();

#define charCodeMask 0x000000FF
/* copied from event.h to conserve space */

 /* get necessary handles, etc. set up */
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 TextH = (**the_data_H).wind_TEH;
 the_extH =  (**the_data_H).the_extents;
 the_ch =  (char) (msg & charCodeMask);
 e_cnt = (**the_data_H).ext_cnt;
 
 in_extent = chk_extent(TextH, the_extH, e_cnt);
 TEKey(the_ch, TextH);
 (**the_data_H).dirty = true;

 /* now adjust extent if necessary */
 if (in_extent)
 {
 if (the_ch==BS) /* backspace - shrink extent */
 {
 curr_ext.ext_end -= 1;
 }
 else   /* insertion - expand extent */
 {
 curr_ext.ext_end += 1;
 }
 }
}

/**
 * Routine: do_paste
 *
 * This is the routine that carries out the “paste” editing function. 
It uses the normal TE routine to manipulate the text. In addition, it 
checks  the source extents to see if any of them are affected by it, 
and if so,it will post the changed extent to the IAC driver for everybody 
else’s use. */

# define __SEG__ Main
void  do_paste()
{
short   this_ed; /* edition (updated) */
short   hit_ext; /* extent affect by this */
short   i;/* scratch */
short   delta;   /* change in extent size */
short   strt, endd;/* range in TE record */
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* hand to extent block */
TEHandleTextH;   /* The TextEdit handle */
Handle  ext_data;/* data for extent */

short   iac_code = noErr; /* result from IAC call */
Boolean in_extent = false;

extern  Boolean  chk_extent();/* sets “current extent”  */

 /* get necessary handles, etc. set up */
 the_data_H = (win_dataH) GetWRefCon (myWindow);
 TextH = (**the_data_H).wind_TEH;
 the_extH =  (**the_data_H).the_extents;

 /* Check extents before actually cutting. Remember which one and we’ll 
update the edition after doing the paste... */
 in_extent = chk_extent(TextH, the_extH, (**the_data_H).ext_cnt);

 TEPaste (TextH);
 (**the_data_H).dirty = true;

 /* if needed, write revised extent to IAC driver */
 if (in_extent)  /* this paste affects the extent */
 {
 /* adjust range for extent */
 /* write to driver */
 this_ed = curr_ext.ed_level;
 /* data setup here */
 iac_code = iac_write_data((**the_data_H).doc_ID, 
   curr_ext.hat_check, 
   &this_ed,1,ext_data);

 curr_ext.ext_end += delta; /* now update extent */
 curr_ext.ed_level = this_ed;
 }
}

/**
 * Routine: kill_extent
 *
 * This removes the current extent from the table and compresses the 
table. The count and “active flag” are also updated. */

# define __SEG__ Main
void  kill_extent()/* always removes current extent */
{
win_dataH the_data_H;/* data associated with a window */
extentH the_extH;/* handle to extent block */
exTable ext_recs;
long    the_doc; /* local copies due to memory mashing */
short   slot_ID, h_check;
short   e_cnt, startt;
short   i;

short   iac_code = noErr; /* result from IAC call */

extern  Boolean  ext_active;

 the_data_H = (win_dataH) GetWRefCon (myWindow);
 the_extH =  (**the_data_H).the_extents;
 e_cnt = (**the_data_H).ext_cnt;
 startt =  curr_ext.ext_strt;

 ext_recs = *the_extH;    /* point to table base */
 for (i=0; i<e_cnt; i++)
 {
 if (ext_recs[i].ext_strt == startt) /* found current extent */
 {
 iac_code = iac_remove_dependency((**the_data_H).doc_ID,
  (**the_data_H).the_slot, 
  ext_recs[i].hat_check);
 BlockMove(&ext_recs[i+1],/* move tail down */
   &ext_recs[i],
   (e_cnt-i)*sizeof(extent));
 (**the_data_H).ext_cnt -= 1;
 (**the_data_H).dirty = true;
 break;
 }
 }
 ext_active = false;
}
{12}
Listing: IAC.c

/***

 * File:IAC.c
 * Package: Inter Application Communications
 *
 * Description:  This is the interface package to the driver for 
 * the use of application programs.
 * Author(s):
 * FEA  6/19/88  */

# include <types.h>
# include <files.h>
# include <memory.h>
# include <osutils.h>
# include <serial.h>
# include <iac.h>
# include <dialogs.h>

# defineMIN_BLK_SIZE 0xC
# defineDEBUG  false
# define  IAC_ERR_ALRT  257

static  short  iac_ref_num;

/**
 * Routine: iac_open
 *
 * The IAC driver is opened by this call and the ioRefNum saved so
 it doesn’t need to be passed with all calls. A null value is returned
 if the open is successful; otherwise the operating system error is
 returned. */

short iac_open()

{
SysEnvRec theWorld;/* ALMOST complete knowledge about world */
THzs_ZoneP, a_ZoneP; /* so can check zone adjacency */
OSErr   the_err;

short   result = 0;

 the_err = SysEnvirons(1, &theWorld);
 if (theWorld.systemVersion < 0x0420)/* too early! */
 {
 result = EARLY_SYS; /* “no multifinder” */
 }
 else
 {
 /*check for multifinder active by checking if system zone is
 adjacent to application zone, which never happens under
 MultiFinder. */

 s_ZoneP = SystemZone();
 a_ZoneP = ApplicZone();
 if ( ((long) s_ZoneP->bkLim + MIN_BLK_SIZE) == (long) a_ZoneP )
 {
 result = EARLY_SYS; /* zones adjacent - no multifinder */
 }
 else /* now try to actually open the IAC driver */
 {
 SysBeep(1);
 result = OPENDRIVER(“\p.IAC”, &iac_ref_num);
 }
 }

 return(result);
}

/**
 * Routine: iac_add_dependency
 *
 * This is used to inform the IAC driver that a new dependency has been 
established and should be added to its internal tables. A null value 
is returned if the open is successful; otherwise the operating system 
error is returned. */

short iac_add_dependency(doc_id, slot_id, hat_check, edition)
 long *doc_id; /* identifies source document (permanent) */
 short  *slot_id;/* identifies source document (session) */
 short  *hat_check;/* extent identifier */
 short  *edition;/* how many times extent has changed (session) */

{
IOParam the_blk;
OSErr   the_err;

struct {
 short  func;
 long doc_id;
 short  slot_id;
 short  hat_check;
 short  edition;
} my_params;

# if DEBUG
 return(the_err=noErr);   /* short circuit for testing */
# endif

 my_params.func = 1; /* set up private parameter block */
 my_params.doc_id = *doc_id;
 my_params.slot_id = *slot_id;
 my_params.hat_check = *hat_check;

 the_blk.ioCompletion = nil; /* set up driver param block */
 the_blk.ioRefNum = iac_ref_num;
 the_blk.ioBuffer = &my_params;
 the_blk.ioReqCount = sizeof(my_params);
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset = 0;
 the_err = PBWrite(&the_blk.qLink, false);   
 /* add the dependency */

 if (the_err == noErr)    /* update output parameters */
 {
 *doc_id = my_params.doc_id;
 *slot_id = my_params.slot_id;
 *hat_check = my_params.hat_check;
 *edition = my_params.edition;
 }

 return(the_err);
}

/**
 * Routine: iac_complete_dependency
 *
 * This is used to inform the IAC driver of a document that is interested
 in a particular dependency. A null value is returned if the open is
 successful; otherwise the operating system error is returned. */

short iac_complete_dependency(doc_id, slot_id, hat_check)
 long *doc_id; /* identifies source document (permanent) */
 short  *slot_id;/* identifies source document (session) */
 short  *hat_check;/* extent identifier */

{
IOParam the_blk;
OSErr   the_err;

struct {
 short  func;
 long doc_id;
 short  slot_id;
 short  hat_check;
} my_params;

# if DEBUG
 return(the_err=noErr);   /* testing without MF */
# endif

 my_params.func = 2; /* set up private parameter block */
 my_params.doc_id = *doc_id;
 my_params.slot_id = *slot_id;
 my_params.hat_check = *hat_check;

 the_blk.ioCompletion = nil;/* set up driver param block */
 the_blk.ioRefNum = iac_ref_num;
 the_blk.ioBuffer = &my_params;
 the_blk.ioReqCount = sizeof(my_params);
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset = 0;
 the_err = PBWrite(&the_blk.qLink, false);

 if (the_err == noErr)    /* update output parameters */
 {
 *doc_id = my_params.doc_id;
 *slot_id = my_params.slot_id;
 *hat_check = my_params.hat_check;
 }

 return(the_err);
}

/**
 * Routine: iac_remove_dependency
 *
 * This is used to inform the IAC driver that a document is no longer 
interested in a particular dependency. If the document is the original
 source all “targets” will be informed that there is no longer any such
 extent; if all targets lose interest the source will be informed that 
it no longer needs to update the extent. */

short iac_remove_dependency(doc_id, slot_id, hat_check)
 long doc_id;    /* identifies source document (permanent) */
 short  slot_id; /* identifies source document (session) */
 short  hat_check; /* extent identifier */

{
IOParam the_blk;
OSErr   the_err;

struct {
 short  func;
 long doc_id;
 short  slot_id;
 short  hat_check;
} my_params;

# if DEBUG
 return(the_err=noErr);   /* testing without MF */
# endif

 my_params.func = 3; /* set up private parameter block */
 my_params.doc_id = doc_id;
 my_params.slot_id = slot_id;
 my_params.hat_check = hat_check;

 the_blk.ioCompletion = nil; /* set up driver param block */
 the_blk.ioRefNum = iac_ref_num;
 the_blk.ioBuffer = &my_params;
 the_blk.ioReqCount = sizeof(my_params);
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset = 0;
 the_err = PBWrite(&the_blk.qLink, false);   

 return(the_err);
}

/**
 * Routine: iac_available_dependency
 *
 * This sets the indicated extent as the “available extent” to be used
 as the source for future defaulted “complete_dependency”  calls.  */

short iac_available_dependency(doc_id, hat_check)
 long doc_id;    /* identifies source document (permanent) */
 short  hat_check; /* extent identifier */

{
IOParam the_blk;
OSErr   the_err;

struct {
 short  func;
 long doc_id;
 short  hat_check;
} my_params;

# if DEBUG
 return(the_err=noErr);   /* for testing without MF */
# endif

 my_params.func = 4; /* set up private parameter block */
 my_params.doc_id = doc_id;
 my_params.hat_check = hat_check;

 the_blk.ioCompletion = nil;/* set up driver param block */
 the_blk.ioRefNum = iac_ref_num;
 the_blk.ioBuffer = &my_params;
 the_blk.ioReqCount = sizeof(my_params);
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset = 0;
 the_err = PBWrite(&the_blk.qLink, false);   /* this dependency is now 
“available” */

 return(the_err);
}

/**
 * Routine: iac_status
 *
 * This allows the application program to find out what’s going on. */

short iac_status(slot_id, vers_id, doc_count, extent_count)
 short  slot_id; /* identifies the inquiring document (session) */
 short  *vers_id;/* driver version*100 */
 short  *doc_count;/* count of active documents */
 short  *extent_count;  /* count of extents relevant to inquiring doc 
*/

{
IOParam the_blk;
OSErr   the_err;

struct {
 short  func;
 short  slot_id;
 short  vers_id;
 short  doc_count;
 short  extent_count;
} my_params;

# if DEBUG
 return(the_err=noErr);   /* for testing without MF */
# endif

 my_params.func = 5; /* set up private parameter block */
 my_params.slot_id = slot_id;

 the_blk.ioCompletion = nil;/* set up driver param block */
 the_blk.ioRefNum = iac_ref_num;
 the_blk.ioBuffer = &my_params;
 the_blk.ioReqCount = sizeof(my_params);
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset = 0;
 the_err = PBRead(&the_blk.qLink, false);    /* read IAC status*/

 if (the_err == noErr)    /* update output parameters */
 {
 *vers_id = my_params.vers_id;
 *doc_count = my_params.doc_count;
 *extent_count = my_params.extent_count;
 }

 return(the_err);
}

/**
 * Routine: iac_census
 *
 * This provides identifying info for all registered extents.
 */

short iac_census(extent_count, extent_info)
 short  *extent_count;  /* count of extents registered */
 info_tblPextent_info;  /* Ptr to table of info for each extent */

{
IOParam the_blk;
OSErr   the_err;
short   i;

struct {
 short  func;
 short  extent_count;
 info_rec extent_info[MAX_EXTS];
} my_params;

# if DEBUG
 return(the_err=noErr);   /* testing without MF */
# endif

 my_params.func = 6; /* set up private parameter block */

 the_blk.ioCompletion = nil;/* set up driver param block */
 the_blk.ioRefNum = iac_ref_num;
 the_blk.ioBuffer = &my_params;
 the_blk.ioReqCount = sizeof(my_params);
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset = 0;
 the_err = PBRead(&the_blk.qLink, false);    /* IAC status */

 if (the_err == noErr)  /* update output parameters */
 {
 *extent_count = my_params.extent_count;
 BlockMove (&my_params.extent_info[0],
    (Ptr) extent_info,
    (long) my_params.extent_count * sizeof(info_rec));
 }

 return(the_err);
}

/**
 * Routine: iac_write_data
 *
 * This updates the data for the specified extent, resulting in a new
 change level. */

short iac_write_data(doc_id, hat_check, edition, fmt_count, ext_data)
 long doc_id;    /* identifies source document (permanent) */
 short  hat_check; /* extent identifier */
 short  *edition;/* how many times extent has changed (session) */
 short  fmt_count; /* number of formats being written */
 Handle ext_data;/* Handle to actual data */

{
IOParam the_blk;
OSErr   the_err;

struct {
 short  func;
 long doc_id;
 short  hat_check;
 short  edition;
 short  fmt_count;
 Handle the_dataH;
} my_params;

# if DEBUG
 return(the_err=noErr);
 /* for testing without MF */
# endif

 my_params.func = 7;
 /* set up private parameter block */
 my_params.doc_id = doc_id;
 my_params.hat_check = hat_check;
 my_params.fmt_count = fmt_count;
 my_params.the_dataH = ext_data;

 the_blk.ioCompletion = nil;
 /* set up driver param block */
 the_blk.ioRefNum = iac_ref_num;
 the_blk.ioBuffer = &my_params;
 the_blk.ioReqCount = sizeof(my_params);
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset = 0;
 the_err = PBWrite(&the_blk.qLink, false);
 /* update dependency */

 if (the_err == noErr)
 /* update output parameters */
 {
 *edition = my_params.edition;
 }

 return(the_err);
}

/**
 * Routine: iac_read_data
 *
 * This is used to retrieve the actual data for the latest change_levelfor 
the specified extent. The IAC driver will record that the inquiring document 
has read the data. ext_data will be resized by the driver to hold the 
data.  */

short iac_read_data(doc_id, slot_id, hat_check, edition, fmt_pref,fmt_code, 
ext_data)
 long doc_id;    /* identifies source document (permanent) */
 short  slot_id; /* identifies the source document (session) */
 short  hat_check; /* extent identifier */
 short  *edition;/* how many times extent has changed (session) */
 long fmt_pref[3]; /* preferred formats, descending desirability */
 long *fmt_code; /* format returned to caller */
 Handle ext_data;/* Handle to actual data */

{
IOParam the_blk;
OSErr   the_err;

struct {
 short  func;
 long doc_id;
 short  slot_id;
 short  hat_check;
 short  edition;
 long fmt_pref[3];
 long fmt_code;
 Handle ext_data;
} my_params;

# if DEBUG
 return(the_err=noErr);   /* for testing without MF */
# endif

 my_params.func = 8; /* set up private parameter block */
 my_params.doc_id = doc_id;
 my_params.slot_id = slot_id;
 my_params.hat_check = hat_check;
 my_params.edition = *edition;
 my_params.fmt_pref[0] = fmt_pref[0];
 my_params.fmt_pref[1] = fmt_pref[1];
 my_params.fmt_pref[2] = fmt_pref[2];
 my_params.ext_data = ext_data;

 the_blk.ioCompletion = nil;/* set up driver param block */
 the_blk.ioRefNum = iac_ref_num;
 the_blk.ioBuffer = &my_params;
 the_blk.ioReqCount = sizeof(my_params);
 the_blk.ioPosMode = fsFromStart;
 the_blk.ioPosOffset = 0;
 the_err = PBRead(&the_blk.qLink, false);
 /* read the data */

 if (the_err == noErr)
 {
 *edition = my_params.edition;
 *fmt_code = my_params.fmt_code;
 }

 return(the_err);
}
{13}
Listing:  SysEnvs.a

;
;Glue for SysEnvirons that was skipped by Apple
;F. Alviani
;7/88
;
 
 INCLUDE‘TRAPS.A’;Include Memory manager macros.
 PRINT ON
SYSENVIRONS FUNC  EXPORT
 movea.l  4(sp),a0 ;a0 contains ptr to world record
 move.w 8(sp),d0 ;d0 contains desired version
 _SysEnvirons
 movea.l(sp)+,a0 ;get return addr, remove
 lea    6(sp),sp ;pop parameters
 move   d0,(sp)  ;put result in place
 jmp    (a0);  go home
 DC.B ‘SYSENVIR’
 
 END
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.