TweetFollow Us on Twitter

MPW, C
Volume Number:4
Issue Number:1
Column Tag:MPW Workshop

Using Consulair C & TML in MPW

By William Powell, Salt Lake City, UT

William Powell is a candidate for a doctorate degree in Geophysics from the University of Utah, and is responsible for the design, documentation and delivery of software used to operate the Voyager spacecraft while a technical assistant at the Jet Propulsion Laboratory in Pasadena. He is presently doing research in the thermal physics of the Colorado Plateau

I am highly resistant to changing development systems, but after after all the fuss over MPW and reading J. West’s MacTutor article [vol. 3, no. 2] on the resource tools in the Macintosh Programmer’s Workshop (MPW), I decided that I couldn’t live without this product. Along with everyone else, I have frequently experienced frustration from the limitations of earlier resource-building programs. I have also wished for a programmable shell on my Mac for certain data processing operations which I used to perform very conveniently on Unix systems. So I broke down and purchased the APDA release of MPW, and a hard disk to hold it. I have subsequently made some efforts toward integrating the MPW shell with older applications. I hope that some of my discoveries and command scripts may be generally useful to others who are using applications with MPW.

The programmable shell and tools of Apple’s Programmer’s Workshop (MPW) give a new dimension of flexibility to Macintosh development. The MPW brings the Macintosh a long step closer to the capabilities of more established development systems such as those available in the Bourne shell or C shell of Unix systems. This necessarily means that MPW has made a long step awayfrom earlier Macintosh application-type development systems. MPW introduces a new class of compiled program, the MPW Tool, which interacts with its shell more intimately than earlier applications interact with the Finder and similar shells.

Bridging the gulf between the MPW shell and earlier Finder application development systems is the topic for this article. I will present examples for my development systems of choice, Consulair MacC and TML Pascal. There are three core issues in joining the MPW shell and application-type development systems. First is the problem of conveniently running applications in concert with programmable shell scripts and the automatic make utility. Second is the problem of writing a new class of program, called a MPW tool, using a development system which does not contain provision for such a thing. Third is object file compatibility with the MPW standard. This article will concentrate on the first item.

Many of the techniques discussed here are relevant to almost any programming of the MPW shell and will be of interest even if the reader does not need the sample command scripts for running development applications. This article includes examples of advanced MPW editor commands, examples of complicated shell quoting problems, and examples showing the use of shell variables. Information here about launching applications from the shell and about the distinction between windows and files is not found in the MPW Reference document. I also use the Make tool three times in this article -- only one time involving building a program! I will assume that the reader is slightly familiar with the shell and has access to the MPW Reference document for more detailed background.

Motivation

Why did I make the effort to mix older development systems into MPW when an assembler and C and Pascal compilers are already being distributed? One consideration is budget; it is hard to justify the cost of anotherC or Pascal compiler when the ones I have actually work just fine. Source portability is never perfect across Macintosh compilers and I haven’t felt too anxious about converting yet another program to yet another compiler. Also, as noted by F. Alviani [MacTutor vol.3, no.4] the MPW system contains its own quirks, such as segmentation established in compilation rather than in linking.

There may be hardware considerations as well. The MPW documentation suggests that the MPW C compiler needs every byte of a 1M machine. This should be a serious consideration for those of us who develop in C on the pre-1987 Macs. It is good that the MPW system is forward looking in terms of newer machines, but many of us can’t (or won’t) upgrade our hardware and software as frequently as it is offered to the market.

On the other hand, the MPW shell offers the most flexible programmable shell and scripting abilities available for the Mac. The text editing features of the MPW shell are quite convenient, and more significantly, programmable. The first complete and extendable set of resource tools resides in the MPW. The programmable shell allows you to do things “your way” instead of locking you into someone else’s concept of good development tools. It also provides a suitable environment for many less human-interactive data processing needs.

So there are excellent reasons to add the MPW shell to the set of tools available to most Mac programmers and there are good reasons for some of us to keep our older development systems. The puzzle now is to find convenient ways to use the power and flexibility of the MPW shell in conjunction with older Finder-based development applications which have no facility to interact with the shell.

Applications and the MPW shell

The MPW shell is primarily a command interpreter, which parses lines typed by the user and interprets them as some sort of instructions to be performed by the computer. A simple command normally appears as a line of text. The first word on a command line is the name of the command to be performed -- conventionally a verb describing the action performed (e.g. Count, Make, Delete, Open, etc.). Subsequent words on the command line are arguments which either indicate objects of the action (e.g. files to be read or written) or specify modes or options for the action (choices which would be selected with menus or modal dialogs in Macintosh applications). As with most programming languages, syntax is available to produce compound command statements from several simple commands as described here. The shell also allows the setting and testing of values of variables. When I refer to variables in the following discussion, I will use the notation {variableName}.

Commands are implemented in four different ways. Some are built into the code of the MPW shell application itself. Some are separately compiled programs called “MPW Tools” which run from within the shell, much as desk accessories run from within other applications. The name of a ‘TEXT’ file containing one or more lines which the shell can interpret as commands may itself be used as a command. Finally, for compatibility, the authors of the MPW shell allowed the names of Finder applications to be used as commands which cause the applications to start.

Applications and the Finder environment

Normally when an application runs, it starts off with a nearly clean slate. It initializes most of the ROM toolbox managers. The only high-level data in the application areas of memory which are normally available to an application are any resources which were stored on the clipboard before the application was launched and the “Finder Information”. The Finder Information structure is essentially a list of files to be opened or printed by the application. For example, when a user uses shift-clicking to select several documents and an application in the Finder, the names of those documents selected by the user are passed to the application in the Finder Info structure.

There are no standards for passing any other type of information from the launch environment to the application. Some development systems and other systems of several applications use “chaining” instead of “launching” [Inside Macintosh v. II, ch. 2], which allows resources and other data to be passed in the application heap. Unfortunately this requires knowledge of coding details of the applications, and will not be suitable for integrating an arbitrary application with the MPW shell. Applications have been designed under an implicit assumption that all information about operational modes was to be provided interactively by a human user. This leads to the most common impediment in using applications as MPW commands -- it is very rare that applications can complete their job without some human intervention (at the very minimum it is usually necessary for the user to select “Quit” from the application menu to return to the shell). This is unfortunate, because the function and mode of applications such as compilers may be fully defined at the time they are invoked, and the user intervention becomes an unnecessary redundancy.

Figure 2. The ins and outs of MPW shell variable scope.

In spite of the inconsistency between the highly interactive user interface of applications and the more mechanized programmable MPW shell, it is possible to develop procedures to integrate shell commands and applications (particularly development systems) with reasonable success. While it is not possible to eliminate human intervention in the applications, we can minimize the actions which the user must perform. Some of the tips in this article might be viewed as an ad hoc system of “MPW interface guidelines” to this end.

Applications and MPW

As mentioned above, the MPW shell allows us to launch an application by invoking its name as a command. MPW maintains a shell variable called {Commands} with a list of directories (folders) to search for commands. If the application resides somewhere in this command search path list, merely the filename of the application is sufficient as a command. Otherwise the full pathname, with the volume and all subdirectories leading to the application’s resource file must be used as the command name.

The MPW shell provides the standard mechanisms for putting resources on the clipboard or retrieving them from the clipboard. So passing data to or from an application on the clipboard can be performed without any special considerations. The only other information an application might expect from its launch environment is a list of files in the Finder Information structure. The MPW shell has provided a standard method to pass this information. Filenames which are to be passed in the Finder Information to the application are simply listed as arguments on the command line following the application name. Thus, we can fully configure the standard application launch environment using mechanisms explicitly provided by the shell.

Example 1. Launching application with Finder Info.

There may be a number of applications which are commonly launched by the user from within the shell; this will be especially true if the user has completely replaced the Finder with the MPW shell. I have found it convenient to add a “Run” menu to the MPW shell, using the shell command “AddMenu”. This allows selection of my most commonly used applications via menu choices. Such a menu is illustrated in Figure 1.

Most of the items in this menu are straightforward; selecting the menu item simply launches the application because the command field of the “AddMenu” command consisted only of the filename of the application. The last item on the menu, “Word-Paint” is more interesting because it launches a Switcher set of MS Word and SuperPaint. This menu selection launches the application Switcher and passes a Switcher document in the Finder Information. The command line to perform this appears as an argument in the “AddMenu” command which created the menu item:

AddMenu Run Word-Paint 
‘XP40:Word:Switcher XP40:Word:Word+SuperPaint’

The word AddMenu is the command which will add this item to a menu in the MPW shell. Three arguments follow the command name. The first argument is the word “Run” which is the name of the menu we wish to affect. The second argument “Word-Paint” is the text of the item being added to the menu. The curly-d () is followed immediately by a return and signifies that the command continues on the following line. The third argument is the text appearing between single quote marks. This is the command which will be executed when the menu choice is selected. The command consists of two words separated by a space. The first is the full pathname of the application to launch (Switcher) and the second is the full pathname of the document to pass to the application in the Finder Information. The document name in the Word folder is “Word+SuperPaint”; the extra curly-d is a literal quote of the following character, “+”. This quote is necessary because the unquoted “+” symbol is used by the shell as a metacharacter implying certain filename matching rules.

When we select this menu item, the MPW shell closes and Switcher is launched. Switcher then automatically opens the document “Word+SuperPaint” which in turn tells Switcher to launch those two applications. When we quit from both applications and the Switcher, the MPW shell will be restarted.

Of shell variables and search paths

A MPW shell variable is a unique symbolic name that represents storage for a string of characters. Shell commands are available to set the value of a variable, test values, and shell syntax allows the value of a variable to serve as text in a command simply by referring to the variable name. The “Evaluate” command also allows arithmetic operations on strings which can be interpreted as integers. Variables in the shell are entirely analogous to variables in other programming languages. The following examples will use shell variables extensively, so a very brief review of the MPW rules on variables will be helpful.

Values are assigned to variables by the “Set” command:

Set  VarName  “the String value”

The value of a shell variable is substituted in a command line by enclosing the variable name in curly braces, {}. Thus, when we follow the Set command above with the command:

Echo  {VarName}

the output printed is:

the String value

A variable is defined when its name is first encountered by the shell. The Set command can be used to assign a value to the variable; if the first occurrence of the variable is in a command other than “Set”, the null string is used as a default value. The only exceptions to this are some predefined variables which are created by the shell when it is launched [ MPW Reference Manual, Table 3-2].

The scope of variables is an important consideration in designing shell scripts. Normally, the value of a variable is maintained only within the command file where that variable is defined. For the purposes of this discussion, we include commands typed by the user in windows created by the shell as the outermost scope. When a command file is executed by invoking its filename as a command, a new nested level of variable scope is created to correspond to the command. Variables defined at the level where the command file is invoked are not defined within the inner executing command file. Conversely, variables defined within an executing command file are no longer defined when that command file terminates and control returns to the level of invocation. Variables VarA and VarB in Figure 2 offer examples of these scope rules.

Two important commands modify these rules of variable scope. Using the “Execute” command to run a command script file, instead of using the filename as a command, causes the commands in the file to operate within the current scope rather than creating a new level of variable scope. The “Export” command causes a variable and its value to become available to script files which are nested within the level containing the Export command. Using Export to pass the values of variables into nested command scripts is analogous to passing subroutine arguments “by value” in languages such as C and Pascal. Changes in value of exported variables are therefore not passed back out to the enclosing command file. The variable VarC shown in Figure 2 illustrates the behavior of an exported variable in a shell script. Another somewhat anomalous problem of variable scope occurs when applications are started from shell scripts, but I leave further discussion of this to one of the examples below.

Standard search paths for files

Many programs need to be able to find and use auxiliary files to process their input and generate their output. For example, compilers often need to be able to merge “include” files containing system definitions with the current source file, and linkers need to be able to find object code libraries when constructing programs. Searching a large hierarchical file system for these auxiliary files may take a prohibitive amount of time, so it is conventional to supply a “search path” -- a directory or a small list of directories where the desired files are most likely to be found.

The MPW development systems use shell variables to store search paths for various items. The MPW variables are listed in the top section of Table 1 (and are also discussed in the MPW Reference Manual). Some of the variables (marked with “S” in the table) contain the full pathname of a single directory to be searched; other variables (marked “L” in the table) contain a list of full pathnames of several directories, with the pathnames separated by commas. Using this system, changes of the directory structure or locations of files is accommodated simply by adjusting the values of these variables.

Other development systems, such as TML Pascal and Consulair MacC, also need to address this “search path” problem. These systems use special resource files in the System Folder to save the search directories they need, and provide software allowing the user to modify the stored paths. In the rest of this section, we try to integrate these two methods of specifying search paths. We want to follow the MPW standard to save search paths in shell variables (Table 1, lower part) and then use these variables in shell scripts which will create the search path resources expected by the applications. Figure 3 shows a directory pattern for libraries, and indicates what folders are listed in various shell variables from Table 1.

Example 2. TML Search Paths.

When TML Pascal or Linker need to find include files, compiled symbol tables for “Uses” or object file libraries, they first look in the directory containing the source file being processed. If the file is not found there, up to five other directories are searched. The full pathname of each of these five directories is saved as a resource (type ‘STR ‘) in the file “Paths File” in the System Folder. The TML package includes a desk accessory which provides a dialog where the user types directory pathnames for the “Paths File”.

Following the MPW model, the desired search paths are saved as the values of shell variables {TMLPasLib} and {TMLPasInc}. We must now write the pathname strings from these variables as the resources in the “Paths File”. The obvious way to do this is to use the pathname text from the variables to generate an input text file for Rez, the MPW resource compiler; Rez, in turn, will create the ‘STR ‘ resources. Shell script file “MakePPath”, in the listings following the article, accomplishes this.

Table 1. Shell variables for file search paths.

System Var Name List * Purpose

Libraries S MPW runtime libraries

AIncludes L MPW Assembler header files

RIncludes S Type definitions for MPW Rez

MPW CLibraries S MPW C object libraries

CIncludes L MPW C header files

PLibraries S MPW Pascal object libraries

PIncludes L MPW Pascal include files

MacCLib S Folder containing Consulair non-objects, headers in sub folders

MPW TMLPasLib S TML Pascal object libraries

TMLPasInc L more TML object libraries and includes

* S means variable contains only one directory to search

* L means variable contains comma-separated list

MakePPath opens two temporary windows for use as scratch files. Into one window, the pathname list from shell variable {TMLPasInc} is written, so that it can be parsed with shell editor commands. The second window, TMLPathOut, is where we create the input for Rez. In MakePPath, we write the HFS path from variable {TMLPasLib} into string resource 128, and the first four HFS paths from {TMLPasInc} are written into resources 129 - 132. If {TMLPasInc} contains more than four, only the first four are used.

The main novelty in MakePPath is the loop which breaks the comma-separated list of paths from {TMLPasInc} into individual paths using the MPW editing and selection commands; this could also be useful in breaking down other lists in MPW variables. Let’s consider the editor commands which do this. It is assumed that 1)the list of paths has been written as a single line in the file named Tmpfil, and 2)none of the directory names in any of the paths contain a comma.

Before entering the loop, we move the selection in the Tmpfil window (the insertion point) to the beginning of the file, before the first character.

Find • Tmpfil

Then we loop until we find all the paths in the list. The first selection we make inside the loop is:

Find §:/,/ Tmpfil

This selects text from the character after the previous selection to the character preceding the next comma. This selection, if it exists, is a valid path, and we can use the notation Tmpfil.§ to refer to this selection. After we use this selection, the command:

Find §:§!1 Tmpfil

selects the comma following the interesting text. This way when the loop repeats, our next string will not begin with an extraneous comma. If the selection at the beginning of the loop could not be made, then we have reached the last path (which of course will not end with a comma) or there was no text in the variable to begin with. The command:

Find §:  Tmpfil

selects all the text to the end of the file and so selects the last path, if it exists. Since all commands in the shell, including Find, return a status value, we are easily able to test whether each of these selections is successful.

The script file MakePPath is a little more elaborate because it must use only the first four strings from the variable. Also it must write null strings into the ‘STR ‘ resources when fewer than four are specified. After MakePPath has run, the ‘STR ‘ resources it created must still be turned into paths used by the system; the Set Paths desk accessory must be used. When the DA starts, the search paths specified by shell variable are already listed in the DA’s dialog box. The user simply needs to select the “Set” button in the dialog to establish these search paths. The MPW script file is helpful because the version of the DA I have doesn’t recognize Edit menu operations like Cut and Paste. A sometimes useful/sometimes troubling side effect of Set Paths is that most other applications which search the System Folder for help files or other support data will also search the Set Paths directories for those files.

Example 3. Consulair’s Path Manager application.

The Consulair method for specifying search paths is considerably more complicated, flexible and robust than TML’s. There are many classes of files which may be sought in different directories. Consulair provides an application, Path Manager, which takes a user-written text file as input and writes a resource file containing search path information for the applications. The input source is a list of any number of search paths for the various classes of target file. Consulair has not published the format of their path resource, so rather than using Rez, we will use MPW variables holding search paths for libraries and includes to augment a Path Manager input file.

Many of the paths used with the Consulair applications are “source relative”, meaning that the directories to be searched are specified by partial pathnames appended to the directory where the current source file resides, rather than by full pathnames. This is a strong point in the design of the Consulair Path Manager strategy, and because many paths are source relative, many specifications in the Path Manager input will not change when the directory structure changes. The only paths we really need to worry about are the same ones considered in the MPW and TML systems: the locations of libraries and system include files. Therefore I prepare a template file for Path Manager input which contains the relative paths exactly as needed and which includes MPW variable references only for absolute pathnames to library objects and includes. When the paths change, I just need to change the values of the variables, expand the variables in the template file, and send this as input to the Path Manager application.

Let’s consider two sample lines in this template file which specify some directories to search for header files (included .h files) needed by the C compiler. Path Manager input lines look like:

:C_Include*S*S:Includes:
C_Include {MacCLib}CIncludes: {MacCLib}Includes:

The first item on each input line (“C_Include” in this example) indicates the class of sought object for the Path Manager and applications to use. A tab-separated list of directories to search follows the class identifier on each line. In the first line of the example, “ *S” is a Consulair metanotation which refers to the directory which contains the source file which is being compiled. This input line will cause the C compiler to look in the directory containing the source file and in a folder in that same directory which is named Includes. This input line is interpretable “as is” by the Path Manager and does not need to be changed when different source directories are used. More information and examples of Path Manager input can be found in the Consulair documentation.

Figure 4. Shell variable scope anomaly associated with the launching of applications. Shell script FileB is implicitly closed.

The second line of the example fits our present purpose. It lists two directories which will contain system-wide include files. This line uses shell variable {MacCLib} to refer to the two directories as shown in Figure 3. But the Path Manager does not recognize the MPW shell variables, so we must expand the variable in this input line before passing the input file to the application. Shell script MakeCPath accomplishes this task, and launches the Path Manager. Note that the placement of the template file as shown in Figure 3 allows MakeCPath to use variable {MacCLib} to locate the template. If the template isn’t found we will know that the variable is not up to date!

MakeCPath uses the template file as input and writes the text file which will be read by the Path Manager application. The file for the Path Manager is named MacC.Path and placed into the System Folder for later reference. Expanding the variables in the template file without altering any other text requires a little bit of mental contortion and the use of an intermediate scratch window. The first step in the process is to open a window to the template file. In the template window, we literalize any characters which might be mistaken for shell metacharacters. For example, the asterisks in the “*S” entries above must be literalized. The sequence of commands

Find •
Replace -c   /*/ ‘*’

inserts three curly-d’s in front of every asterisk in the template. The three adjacent curly-d’s cause literal interpretation of the asterisk through the next two shell commands which interpret the line. Note that in general, 2n-1 curly d’s will literalize a character n times. Any other MPW shell special characters which appear in the template must be similarly quoted. MakeCPath only literalizes asterisks and tabs; add your own as needed. Note that the curly-d’s are being added directly to the template window. When we are finished with the template, we must close this window with the -n option so the changes won’t be written to the file on the disk.

Once the template is prepared, loop to process it one line at a time. MacTutor masochists who read the code listings first may be mystified at the command

Echo Echo ”‘Catenate §‘” >> “{tmpfile2}”

There may be a prettier way to do it, but this does work. This is an echo command which writes another echo command

as a line in the scratch window. The name of the scratch window is in the variable {tmpfile2}. The Catenate§ inside grave symbols is replaced in the scratch file by the line currently selected in the template file. This catenate command consumes one level of literalization in the template, so * in the template becomes * in the scratch file. Shell variables such as {MacCLib} are not expanded in the scratch file.

When the loop finishes, the scratch file contains one echo command corresponding to each line of the original template file. Execute the commands in the scratch file using

Execute “{tmpfile2}” >> “{outfile}”

and the variable outfile contains the filename MacC.Path which will become the input to the Path Manager application. The echo commands in the scratch file consume one more level of literal quotes (so * becomes * in the output file) and they also expand any shell variables into their values.

MakeCPath contains additional commands which facilitate convenient behavior after the application runs; these features will be considered in the next section. Finally the MakeCPath script launches the Path Manager, passing the file MacC.Path in the Finder Information structure.

Now we run into some of the difficulties associated with applications in shell scripts. Although I passed the input with the Finder Info, the Path Manager (ver. 5 at least) doesn’t notice it. I still have to manually select a menu option, and then locate the text file using a StandardGetFile dialog. The best I can do is make this process as painless as possible. In the MakeCPath script, just prior to launching the application, make the System Folder into the default directory using the Directory command. Now when the Path Manager throws up the StandardFile dialog, it will initially be showing the files in the System Folder. Since MakeCPath wrote the file MacC.Path into the System Folder, we won’t have to search through the directory hierarchy to find it. When the Path Manager generates the PATH resources, I will also have to manually specify the output file in a StandardPutFile dialog, and then manually select Quit from the menus. We’ve saved ourselves some work in editing the input file, but if the application just used the Finder Information all the manual interaction could be eliminated.

Altered states

Now let’s consider the things which happen in the shell environment when we launch an application. In the absence of Switcher-like trickery, we must terminate any current application prior to launching another one. The MPW Shell is itself an application and so must terminate before launching another and must be relaunched when the external application quits. In the parlance of the MPW Reference, MPW is “suspended”, the external application runs, and then MPW is “resumed”. The processes necessary to suspend and resume the MPW shell are performed by shell scripts provided with the MPW system, named (appropriately) Suspend and Resume.

The Suspend script saves all currently defined shell variables, exported variables, command aliases, menu items, the current working directory, and any open windows. Then when the MPW shell is relaunched, the Resume script uses this saved information to return the shell to nearly the same condition as when it was suspended. There is one important difference between the suspended state and the resumed state, which is tersely documented in the MPW Reference. An application may be launched from a nested command script, such as MakeCPath from the preceeding example, but the shell always resumes at the outermost command level. The shell implicitly exits any nested command scripts when suspended.

This is the variable scope anomaly I to which I earlier alluded. The Suspend script saves the names and values of variables at the current level of scope, but the Resume script sets these variables again at the outermost level (see Fig. 4). WARNING: I use this as a “feature”, but future versions of the shell may consider this a “bug”; some of my shell scripts will require minor modification if future releases change this behavior (I am using MPW version 1.0 for the examples of this article). [Current version is 2.0b1. -Ed]

If you launch applications within scripts, as I do, some defensive scripting practices are essential. Any outer-level shell variables you want around after Resume must be exported from the outer level and not modified by nested shells. Any inner-level variables you don’t want around after Resume must be Unexported and Unset in the script file prior to the command which starts the application. Remember that you only need to worry about this when you are using shell scripts like MakeCPath which launch applications.

Often, launching an application is not the final step in the process, but the shell script which launches the application is not restarted from that point. I get around this by setting up commands to be executed by the Resume process. I put these commands into a file and save the name of this file in a shell variable called {PostApp}. I have modified the Resume script file to include the commands:

Execute “{PostApp}”
Set PostApp NullFile

When the MPW shell resumes after an application, commands which I saved prior to suspending can be executed. Variable {PostApp} is reset to a default empty command file which is named “NullFile”. An alternate method that doesn’t depend on the Suspend/Resume shell variable scope anomaly is to save the commands under a specific file name, say “UserResume”. Then the Resume script should contain

Execute “UserResume”
Echo “”> UserResume#Deletes contents

I prefer the first method (as long as it works) because the command file contents stay available for debugging when I am writing a new command script. Note that in either method the changes to file Resume are permanant, and we merely change variable {PostApp} or file UserResume to fit the needs of the moment.

I return to the MakeCPath script for an example of a useful Resume task. Recall that MakeCPath changed the current working directory to the System Folder just prior to launching the Path Manager. This means that the Suspend script will save this directory and Resume will restore the System Folder as the current working directory. This will probably not be the directory from which the user started the MakeCPath script, so when we Resume, we are not where the user expects. A more user-friendly thing to do is to save the original working directory and restore it at the end of the Resume process. The commands which set this up are found near the beginning of MakeCPath. Variable {PostApp} is given the name of the resume command file “UserResume”. The original directory is used in a command stored in file UserResume:

Directory “OldDirectoryName “

Now when MPW resumes, the user will be in the same place in the directory hierarchy as when MakeCPath was invoked. This technique of adding transient commands when resuming from applications has considerable utility and we will see more extensive examples presently.

MPW Make with Applications

The shell scripts developed in the preceding text, while they do provide instructive examples of technique, are only moderately useful. The directory structure and library file locations shouldn’t change too often, and these shell scripts are unlikely to be needed more than a couple of times a year.

Building and maintaining programs, on the other hand, is more of a daily occupation. Within the MPW, these tasks center around the tool “Make”, which is an elegant system of rules to emit commands which depend upon the relative ages of files. If we apply what we learned in the previous examples to this problem, we are rewarded by a system of genuine utility -- an integration of MacC and TML Pascal systems which the developer accesses from entirely within the MPW environment.

Basics of Make

“Make” programs are widely available utilities found in the MPW, in a variety of Mac shareware and commercial offerings and in most Unix--based systems, among others. The traditional use of a Make utility is to keep programs up to date with a minimum of processing, compiling only when source files change and linking only when relocatable objects change. This behavior is controlled by a text file containing 1)dependency rules specifying which files are constructed from which predecessor files and 2)a list of commands which perform the operations necessary to update the target files. Since F. Alviani’s April ’87 MacTutor article elaborated some of the sketchy documentation on Make, I won’t repeat that detail here. An important point in the adaptation of MPW Make for application--based development is that the full range of legal commands can be generated by a make script. This allows us to generate “smart” sequences of commands which do much more than just compile and link (and “Rez”).

The MakeCPath command described earlier provides the simplest possible illustration of Make which does not compile or link. The final output of MakeCPath is a resource file in the System Folder named Paths.Rsrc. This output depends, naturally enough, on the input -- the template file, C.Path.Template, and any shell variables , such as {MacCLib}, which are used in the template file. Dependencies in Make can only be specified with respect to files, not variables, so the values of {MacCLib} and related variables are initialized in the MPW script file “Startup”. “Startup” is a script supplied by the MPW system which is executed when the MPW shell is launched from the Finder. If I change the value of variable {MacCLib} by editing this script file, the dependency of Paths.Rsrc on the variable becomes equivalent to a dependency on file “Startup”. (I put these variables into “Startup” instead of “UserStartup” because I edit file “Startup” much less frequently).

The input to Make specifies these dependencies in a special notation:

Paths.Rsrc  ƒ  Startup  C.Path.TemplateMakeCPath

The line containing the ƒ symbol is the dependency statement. Files listed to the left of the ƒ depend upon (or are made from) files listed to the right of the symbol. Dependent files (left side) need to be updated if they are older than any of the predecessor files (right side). Note that in an actual Make script, you might need to specify the appropriate directories as well; I have omitted this detail for the clarity of the example. The dependency statement may be followed by a list of commands which perform the necessary update. Commands are distinguished in the Make input because each line of a command list begins with whitespace (space or tab characters). In the example above, “MakeCPath” is the command which will update the target, “Paths.Rsrc”.

Example 4. The MixMake command

Here I develop a shell script and Make input which work together to build programs. To effectively use applications with shell scripts, we need an understanding of their behavior in this environment. I start this example, therefore, by cataloging what happens to each application when it is launched from the MPW shell with an appropriate input file specified in the Finder Information structure. MacC (ver. 5) does not use the Finder Info properly, and in fact gives an ID 02 bomb (or worse) when you try from the shell. The Consulair linker will process a link script, but when finished puts up a standard file dialog, and requires user interaction to quit to the shell. If the link fails, the Consulair linker will launch Edit, instead of the shell, if it can find it. Removing the Edit application completely from the disk is helpful in using the MixMake shell script. TML Pascal will compile one file from the Finder Info (even if several were listed). After compiling, it requires the user to click OK buttons, to select other files or to quit manually. The TML linker will use a link script passed in the Finder Info, and then runs the program it built and/or returns to the shell. Amazingly, the Microsoft FORTRAN compiler (ugh) has almost ideal performance in this environment; it compiles the file passed in the Finder Info and returns to the MPW shell, and only requires the user click an OK button if a compile fails.

So far, things are looking a bit dim, but utility applications can save the day. The Exec application, provided with the Consulair system, is a simple utility which runs Consulair applications in sequence according to an input text file. Like the Path Manager, when this is launched from the shell the user must select the input file manually, but it generally will return to the shell when done. Furthermore, the Consulair linker and compiler recognize that they have been launched by Exec instead of the shell, and they accept and process input files and return to the Exec without user intervention. The MPW method simply needs to use this launching indirection through Exec to perform an automated build. The TML applications unfortunately do not change their behavior for Exec. Many other free/cheap utilities (for example, Darin Adler’s Sequencer) will run applications with input files in sequence and may be more appropriate for the non-Consulair applications.

The MixMake command, which builds programs with MacC and TML involves a significant amount of programming beyond the simple Make utility. Since Make will, however, form the heart of the MixMake command, let’s consider first the nature of a MixMake makefile. When the dependency rules indicate that a source must be recompiled or a program relinked, a MixMake makefile generates a line of input for the Exec application instead of a command to run the compiler or linker directly.

A sample makefile for an application is given in the Listings. The default dependency rules, which are applied to any filename with the appropriate filename extension, effectively handle all the compile steps. These default dependency rules may require directory dependency rules as well to operate correctly, but this will tend to vary with each build target. The default compiling rules are:

.Rel ƒ .C
 Delete -i {TargDir}{Default}.Rel
 Echo {DepDir}{Default}.C >>{Cfile}
 Set RunMacC 1
.Rel ƒ  .Asm
 Delete -i {TargDir}{Default}.Rel
 Echo {DepDir}{Default}.Asm >>{Cfile}
 Set RunMacC 1
.Rel  ƒ  .Pas
 Delete -i {TargDir}{Default}.Rel
 Echo ‘Pascal  {DepDir}{Default}.Pas  Exec’
 >>{Efile}
 Set RunTPas 1
.Rsrc  ƒ  .R
 Delete -i {TargDir}{Default}.Rsrc
 Rez {RezOpt} -o {TargDir}{Default}.Rsrc 
 {DepDir}{Default}.R

Variable {Efile} is the file which is being prepared for Exec input. Variable {Cfile} is a filename with extension “.Files” which contains a list of sources to be compiled by the MacC application. Setting the variable {RunMacC} tells the enclosing MixMake shell command to add a single command to the Exec input file which will launch the MacC compiler with the “.Files” file. If resources are to be compiled with Rez, the commands are generated by Make and executed directly by the MixMake script. This arrangement assumes that non-CODE resources are compiled by Rez but the application is assembled from CODE and these resources by a subsequent link step. When a Pascal source is to be compiled, the makefile appends an appropriate command to the Exec input file. Since the Pascal compiler does not interface well with Exec, the user must select (and know in the first place) all the sources to be compiled in Standard File dialogs. When the user selects Quit from Pascal, the Exec processing is terminated and MPW resumes. To complete any MacC compiling and linking, the user would have to run the MixMake command a second time.

A much nicer method uses Adler’s Sequencer and could be adapted most to any application-running utility; a Pascal compiler build rule is implemented as Rez input which is used to produce a Sequencer which will build the desired Pascal files. The user does not need to know which Pascal sources are to be rebuilt; the user just selects Quit in Pascal’s file menu after each compile, and the next out-of-date file is compiled automatically. The final operation by the Sequencer after all Pascal files are compiled is to launch the Exec to handle C compiling and Linking. I have included both versions of MixMake with the source code, but will not describe this one further in the text. The extra effort to use the Sequencer (or similar utility) has significant benefit. The Suspend/Resume scripts take a noticeable time to execute, so we want to do as much work as possible outside the shell for each Suspend/Resume cycle.

The sample Makefile.Template in the listings illustrates the appropriate specifications needed to build a particular file. It adds a directory dependency rule before the default compiler rules. A dependency rule for the link step must explicitly list the link script (“.Link” file), relocatable object files (“.Rel” files) and compiled resources (“.Rsrc” files) which are needed. The command generated by this dependency rule is again an appropriate line of Exec input. Particular care must be taken to prevent building a program with some up-to-date object files and some which are not up-to-date because of compiler errors. The delete command in the compiler default rules described above is executed by the MixMake script and prevents a successful link until the object is successfully rebuilt by the compiler. The MacC compiler rules don’t need to delete obsolete objects because the Exec-MacC system allows a time saving alternative. Each line of Exec input consists of four fields: 1)the application to launch, 2)the input file for the application to process, 3)an application to run if the application in field 1 runs successfully (normally Exec to continue processing), and 4)an application to run if the application in field 1 fails. The line of Exec input which MixMake uses is

C  input.Files  Exec  Done

The application “Done” is a trivial program containing only an ExitToShell command. If any of the files to be compiled or assembled by MacC have errors, the Exec processing terminates before any attempt to link, and the Done program Resumes the MPW shell instead.

It is important not to list the MPW Shell directly as an application in the Exec input. The MPW shell could be launchedby Exec, but then selecting Quit from the MPW File menu resumesthe MPW shell instead of starting the Finder. This is quite confusing, so we don’t do it.

There are few restrictions on MixMake. The MixMake command shell takes user input arguments exactly like Make, and uses a makefile with the appropriate default rules described above to generate an input file (set up to run in the appropriate order) for the Exec application. The Make tool’s options -d, -e, -f can be used in arguments for MixMake. Make options -p, -r, -s, -t, -u should not be passed to MixMake; they may not behave as expected. Options -r, -s and -u can be used by invoking Make instead of MixMake and using the MixMake input dependency file. The key to adapting the sample makescript to a particular purpose is in the list of predecessors (usually for a link rule) and directory dependency rules. It is important to follow the advice in the MPW Reference and assure that directories are specified in default directory dependency rules in precisely the same form (full or partial pathnames) as the corresponding explicit predecessor files. These should also be consistent with the search paths of the applications. The version of MixMake which uses the Sequencer requires that full paths are specified for all directories.

In the simplest implementation, MixMake would simply launch the Exec or another application runner. A more sophisticated MixMake, however, provides Resume tasks for “nice” error handling when the build steps don’t succeed. I’ll finish up the article with a description of commands which help with analyzing diagnostics.

OOPS! What about those errors?

All of the Consulair and TML applications write diagnostic files when compiling or linking fails. A convenient feature to add to the mixed system is a facility to automatically open a bad source listing its corresponding diagnostic file simultaneously. Shell script “CompErr” is just such a “diagnostic viewer”, running sequentially through all bad .C and .Asm sources, then all bad .Pas sources, and finally any failed linker scripts. The information needed by CompErr to tell which are the “bad” sources from the most recent build attempt consists of several files and shell variables. These files and variables are provided by a Resume script which is installed by the MixMake command. A copy of the commands MixMake generates as a Resume task is kept in file “MixMakeResume”. These Resume tasks and the CompErr command are more limited than MixMake itself, and generally work only when all the necessary sources and diagnostics are located in a single directory and if that directory is the current working directory when the MixMake command is invoked. This restriction has not been onerous for me, but if necessary more elaborate Resume and CompErr scripts could circumvent the limitation.

The MixMake Resume commands write files which contain lists of paired source and diagnostic files. The Resume commands also install an “Open Diagnostics” item in the MPW shell File menu that runs the “CompErr” command. The files summarizing the diagnostic file names are scanned sequentially by “CompErr”; each time the menu item is selected, the next pair of source and diagnostic files is opened for the user to view and edit. When all the diagnostic files have been opened, the menu item deletes itself.

The MacC compiler creates a file with the basename of its .Files input and the name extension .Fer, in which it writes a list of files which failed to compile and their corresponding diagnostic files. For Pascal compiler and linker errors, the Resume task needs to explicitly search the directory to create such a list. For example, in a directory containing a Pascal source with extension .Pas, the existence of the same name with extension .Per suggests a failed compilation. Existence of the diagnostic file is not sufficient evidence however; the source might have been corrected since the diagnostic file was created. The Resume task only lists source files which are out-of-date with respect to their corresponding diagnostic files. The Make tool, a real workhorse, assures this. Here are dependency rules for finding bad sources from diagnostic files:

.Pas  ƒ  .Perr
 Echo {DepDir}{Default}.Pas 
 {DepDir}{Default}.Per >>TMLP.Fer
.Link  ƒ  .Lerr
 Echo {DepDir}{Default}.Link 
 {DepDir}{Default}.Lerr >>TMLP.Fer

All the Pascal diagnostics are listed in a file named TMLP.Fer and all the link diagnostics in a file named Link.Fer. The dependency rules above are in a makefile permanantly stored (never accessed by the user). This has the advantage of allowing a general command to be built around it, but the disadvantage that it will only work within the current directory.

CompErr uses these files (from the most recent MixMake build only), opening one source/diagnostic pair for each invocation. CompErr should always be accessed through the “Open Diagnostics ” menu item (“Dial Cmd-E for Errors”) because the menu procedure moves to the directory where the build occurred, opens the files, then returns the user to the current directory. Since CompErr works sequentially through the .Fer files, we need to save the current position in the files after each invocation. Counting line numbers might work, but I have chosen to use the method of saving the current window selection with the file.

In MPW, files and windows into those files are distinct, although intimately related. When we save a window, the selection is saved too. After the window is closed, the saved selection reappears when the window is opened. The Save command is pretty smart, and only writes changes to the disk when the text has changed, not when only the selection changes. The CompErr script forces saving new selections by writing innocuous white space into the .Fer file after each change of selection. The marker for where CompErr is in the file is therefore saved elegantly in the file itself.

OK, let’s finish up with an ugly scenario. It is Friday afternoon, you are about to leave on vacation and a lengthy build has just resulted in 30 or 40 erroneous source files. You don’t particularly want to re-Make the whole shebang just to prime CompErr when you return. NO PROBLEM! If we can have Suspend/Resume tasks, we can also have Quit/Startup tasks.

Two scripts delivered with the MPW system, appropriately enough named “Quit” and Startup, handle special tasks when the user elects to terminate the MPW shell by quitting to the Finder and when the shell is launched from the Finder. I have added routines to Quit and Startup which allow me to preserve any state variables I want between invocations of the shell. In the Quit script, I execute a command file named “QuitPermVars”. In QuitPermVars are commands which save two exported state variables required by CompErr: {MakeErr} and {CompErrDir}. QuitPermVars writes Set commands to reproduce these two variables, corresponding Export commands, and an AddMenu command to install the “Open Diagnostics“ item. These commands are saved in a file named “StartPermVars”. Then the Startup script executes StartPermVars when the shell is launched from the Finder. So you can leave your problems behind, and when you come back with clear and refreshed mind, CompErr will dutifully pitch them out to be solved.

Conclusions

Although it may look a bit ungainly because of the number of small command files, the system I have presented here as an example is quite workable and I actually use it every day. Programming the shell is very challenging compared to other programming because of the global nature of many of the manipulated data and the resulting high data connectivity of “independent” modules. But the rewards of mastering shell programming can be great. Before I attempted this project, in a fit of frustration, I had presumed it would be impossible to integrate these diverse applications with the shell. The flexibility of the MPW should not be underestimated however, and this collection of command scripts provides me with the same level of performance from the development applications as when they stand alone plus an improved text handling and resource handling facility.

I should add that the systems integration designed into the Consulair C applications contributes a greatly to the effectiveness of my scheme. The behavior of the various applications (Consulair’s, TML’s and others I’ve tried) could have been better for use from the shell however. A few simple considerations can insure smooth integration of applications and MPW shell.

Making applications MPW friendly

First and foremost, applications which may be launched from the MPW shell (or other non-Finder shells, like A-UX?) should handle the Finder Information structure. No assumptions should be based on Finder behavior (for example, assuming that documents and application must reside in the same folder when they have different file creator words).

Secondly, applications with strongly modal behavior should probably have the ability to read an input file to set those modes and perform its action. An application which can do its job with just one or two mouse clicks by the user in menus or dialog boxes has strongly modal behavior, and compilers are an excellent example. There is unfortunately no way to establish a standard, but the following suggestion should provide a sufficiently convenient interface with the MPW shell. If you have a strongly modal application, consider using a TEXT file, perhaps with a distinctive name or filename extension, as an optional way to set the modes and actions to be performed. And finally, when an application can be controlled modally in this way, provide an option to Quit or Transfer without user intervention. Yes, I know this flies in the face of the User Interface Guidelines, but the example of this article shows that the User Interface is not always the Best Interface.

A challenge to future MacTutor authors

The MPW system provides a Help tool which provides on-line documentation for shell commands. In the listings for this article I have included a script named Man (for Manual) which extends the help procedure to search both the default help file and a second help file named Local.Help. The Man procedure also uses the Canonical spelling tool to convert your standard command aliases to the command names known to the system. Just put an alias and corresponding command on a line in file Alias.Dict.

With the Man command, I have provided a Local.help file with entries for the command scripts developed in this article. Hopefully the command scripts and tools in future MacTutor articles can include relevant help file entries with their source code contributions.

Listings
###file ReadMe
###ReadMe - instructions for installing 
###MixMake and related commands in your 
###MPW system.
###W.G. Powell 1987

###Because parts of the MPW system will usually
###be scattered throughout a number of HFS
###folders (directories) the following guide
###will be helpful in adding the sources 
###provided here. You may need to customize file 
###paths in some of the commands to match your
###directory arrangement.

###Into the MPW directory (the folder containing
###the MPW Shell application itself, and referred
###to by shell variable {MPW}, place the files
###contained in the source disk folder “for MPW folder”
###Local.Help
###QuitPermVars
###StartPermVars
###:MakeSupport:  the folder and its contents:
###MakePErrs.mk
###Mixmake.Template.1
###Mixmake.Template.2
###MixMakeResume
###Paths.mk

###Into the Tools directory (or any directory listed
###in the MPW command search path (Shell variable
###{Commands} ), place the files contained 
###in the source disk folder “for Tools folder”
###Alias.Dict
###CompErr
###MakeCPath
###MakePPath
###Man
###‘MixMake ver.1’
###‘MixMake ver.2’
###NullFile
###UpdatePaths
###If you use Darin Adler’s Sequencer with MixMake,
###rename version 2 “MixMake”.
###If you use Consulair/MDS Exec only with MixMake,
###rename version 1 “MixMake”.
###Use the appropriate template in directory
###{MPW}MakeSupport: as a guide in preparing
###your own MixMake makefiles.

###Into the Applications directory (or any directory 
###listed in the MPW command search path 
###(Shell variable {Commands} ), place the applications
###you will be using with the system.
###Consulair applications:
###C
###Exec
###Link
###‘Path Manager’
###TML applications:
###Pascal
###‘TML Link’
###Other applications
###Sequencer #if used
###Done # supplied with this disk
###Note that if both TML and Consulair
###Linkers are present, one must be
###renamed.
###Also, since TML Desk Acc. “Set Paths”
###is rarely used, I install it in the
###MPW Shell application, instead of a
###precious System File slot.

###The following changes should be made to the 
###scripts supplied with the MPW shell

################################################

###   Add these commands to file Startup
###Modify to match your directory hierarchy.

#  {PostApp} - name of command to execute when
# MPW is RESUMED, after running a Finder application (WGP)
 Set PostApp “NullFile” 
 Export PostApp
 
#  {MacCLib}is directory for MacC includes and Libs 
 Set MacCLib {Libraries}MacC:
 Export MacCLib
 
#  {TMLPasLib} is directory for TML Pascal interfaces, libs 
 Set TMLPasLib “{Libraries}TMLPas:Pascal System TML:”
 Export TMLPasLib
 
#{TMLPasInc} - Directories to search for 
#TML Pascal interface files
 Set TMLPasInc “{Libraries}TMLPas:”
 Export TMLPasInc
 
##################################################
###Add these commands to file UserStartup
###Modify to match your directory hierarchy.

# Recover values of saved state variables
 Execute “{MPW}StartPermVars”
 
##################################################
###Add these commands to file Quit
###Modify to match your directory hierarchy.

###Values of variables below are saved 
###between invocations of the shell
 Execute “{MPW}QuitPermVars”
 
##################################################
###Add these commands to the end of file Resume
###Modify to match your directory hierarchy.

#  System for nice return from applications
#The shell variable “PostApp” contains a command to 
#Execute when resuming.  This variable is reset to a 
#default so unexpected results are avoided when another 
#   application is run.
 Execute “{PostApp}”
 Set PostApp “NullFile”

##################################################

###File MakePPath
# Try using Rez to write path info to the TML path file
# in the system folder.  Basically, just need to expand the 
# TMLPasLib and TMLPasInc vars and write some Rez input
# with it.
#W.G. Powell 1987for MacTutor

# Nonzero command status doesn’t mean errors, 
# so don’t terminate
Set __oldExit {Exit}
Set Exit 0
# Open temporary windows
Open -n -t Tmpfil
Open -n -t TMLPathOut
# Write Rez input using shell’s TML path variables
Echo “#include ”Types.r””>TMLPathOut
# Create STR resource for single path in {TMLPasLib}
echo “resource ’STR ’ (128) { “>> TMLPathOut
echo “”{TMLPasLib}””>> TMLPathOut
echo “};”>> TMLPathOut
# Put list of paths from {TMLPasInc} into a window
echo {TMLPasInc} > Tmpfil
# Break comma-separated list into individual paths
Find • Tmpfil
Set IDno 129
Loop
 If ({IDno} < 133)
 Find §:/,/ Tmpfil 
 If ({Status} == 0)
 echo “resource ’STR ’ ({IDno}) {“>>TMLPathOut
 echo “”`Catenate Tmpfil.§`””>>TMLPathOut
 echo “};”>>TMLPathOut
 Set IDno ‘Evaluate ({IDno} + 1)‘
 Find §:§!1 Tmpfil 
 Else
 Find §:  Tmpfil
 If ({Status} == 0)
 echo “resource ’STR ’ ({IDno}) {“>>TMLPathOut
 echo “”`Catenate Tmpfil.§`””>>TMLPathOut
 echo “};”>>TMLPathOut
 Set IDno ‘Evaluate ({IDno} + 1)‘
 Loop
 If ({IDno} < 133)
 Echo “resource ’STR ’ ({IDno}) {””};”
 >>TMLPathOut
 Set IDno ‘Evaluate ( {IDno} + 1)‘
 Else
 Break
 End
 End
 Else
 Loop
 If ({IDno} < 133)
 Echo “resource ’STR ’ ({IDno}) {””};”
 >>TMLPathOut
 Set IDno ‘Evaluate ( {IDno} + 1)‘
 Else
 Break
 End
 End
 End
 Break
 End
 Else
 Break
 End
End
# Use Rez to write the Paths file.
# Close temporary windows.
Close -n Tmpfil
Rez -o “{SystemFolder}Paths File” -t ‘ZSYS’ -c ‘MACS’ 
 TMLPathOut
Close -n TMLPathOut
Set Exit {__oldExit}
Unset __oldExit
Exit 0 
############################################

###File MakeCPath
###MakeCPath - MPW Shell Script
###Set up input for Consulair Path Manager
### using appropriate MPW shell variables.
###   William G. Powell 1987for MacTutor

#Save current working directory 
#and return to it after Path Manager quits.
Set PostApp ””{MPW}”UserResume”
Echo Directory ”‘Directory‘”> “{MPW}UserResume”
Echo “Delete -i MakePtOut”>> “{MPW}UserResume”
Echo Unset outfile >> “{MPW}UserResume”
#Specify the input template file
Set infile “{MacCLib}C.Path.Template”
#Place the output text file (input for Path Manager)
#into the system folder where easily found
Set outfile “{SystemFolder}MacC.Path”
#Assign temporary file to window and clear it.
Set tmpfile2 “{MPW}tmpfile2”
Open -t -n “{tmpfile2}”
Clear •:  “{tmpfile2}”
#Open the template window
Set _oldExit {Exit}
Set Exit 0
Open -t “{infile}”    Dev:Null
If {Status} != 0
 Alert “Cannot Find/Open the input template file.n
Check shell variable MacCLib, currentlyn{MacCLib}”
 Close -n “{tmpfile2}”
 Set PostApp “NullFile” # Reset default Resume tasks
 Exit 2
End
Set Exit {_oldExit}
Unset _oldExit
#Add necessary quote characters to the template file.
Find •
Replace -c   / / ‘‘ # tab characters
Find •
Replace -c   /*/ ‘*’
#Loop to copy each line of input,
#expanding variables where necessary.
Set loopend ‘count -l “{infile}”‘
Set loopcnt 1
Loop
 If {loopcnt} > {loopend}
 break
 End
 Find {loopcnt}   # Select next line of input
 # Write command into temporary window which will 
 # expand shell variable paths when writing to {outfile}
 Echo Echo ”‘Catenate §‘”>> “{tmpfile2}”
 Set loopcnt ‘Evaluate ( {loopcnt} + 1 ) ‘# increment
End
# Don’t terminate script file on errors below - the possible
# errors are not important enough!
Set _oldExit {Exit}
Set Exit 0
# Open, clear, and write the output file
Open -t -n “{outfile}”
Clear •:  “{outfile}”
Execute “{tmpfile2}” >> “{outfile}”
# Close all working windows, saving only the output.
#Close -n “{tmpfile2}”
Close -n “{infile}”
Close -y “{outfile}”
# Remove all unneeded variables.
Unset infile
Unset tmpfile2
Unset loopend
Unset loopcnt
# Change ”exit on error“ flag back to original value
Set Exit {_oldExit}
Unset _oldExit

# Change default directory to System Folder 
Directory “{SystemFolder}”
# Execute the Path Manager, passing our new file
# in the Finder Information structure.
“Path Manager” “{outfile}”

Exit 0  # Control should never reach this statement
#########################################

###File Paths.mk
# Makefile to ensure that Consulair and TML search paths are  
# up to date with appropriate shell variables
#W. Powell1987 for MacTutor

#Variables containing search paths for library files and 
#include files are defined in the Startup script so we
#want to rebuild when the Startup file is changed.

### TML Pascal Paths File
“{SystemFolder}Paths File”ƒ “{MPW}Startup”
 MakePPath
 
### Consulair MacC Path.Rsrc File
“{SystemFolder}Paths.Rsrc”ƒ “{MPW}Startup” 
 “{MacCLib}C.Path.Template”
 MakeCPath
############################################

###File MixMake ver.1
###MixMake version 1.  
###Uses only Consulair Exec application runner
### Script file to build programs with
### MacC or TML systems
###W.G. Powell 1987for MacTutor

### Initialize variables
#Exec and MacC input and scratch files
Set Efile “Exec.Job”
Set Cfile “MacC.Files”
Set Lfile “Link.Tmp”
Delete -i “{Efile}” “{Cfile}” “{Lfile}”
#Application run flags
Set RunMacC 0
Set RunLink 0  # Consulair
Set RunTMLLink 0
#Make’s working directory
Set MakeWD “`Directory`”
Export MakeWD

### Run the Make tool
make {“Parameters”} > MakeOutApps   Dev:StdOut

### Construct the input for the Exec application
Execute MakeOutApps
Delete MakeOutApps
If ({RunMacC} == 1)
 Echo “C{Cfile}  ExecDone”>>”{Efile}”
End
If ({RunLink} == 1 || {RunTMLLink} == 1)
 Catenate “{Lfile}” >>”{Efile}”
 Delete -i “{Lfile}”
End
Unset RunMacC
Unset RunLink
Unset RunTMLLink
Unset Lfile
Unset Cfile
Unset MakeWD

### Set up the Resume tasks for after the applications
### Primarily for error handling
Set PostApp “{MPW}UserResume”
Echo “Unset Efile” >”{MPW}UserResume”
Catenate  “{MPW}MakeSupport:MixMakeResume” >>”{MPW}UserResume”

### If Diagnostic menu item is still around from
###an earlier build cycle, delete it.
Set __oldExit {Exit}
Set Exit 0
DeleteMenu File “Open Diagnostics ”   Dev:Null
Set Exit {__oldExit}
Unset __oldExit

### Run applications using Consulair’s Exec
Exec “{Efile}”
########################################

###File MixMake ver.2
###MixMake version 2.  
####  Uses Sequencer application and Consulair Exec

### Script file to build programs with
### MacC or TML systems
###W.G. Powell 1987for MacTutor

### Initialize variables
#Exec and MacC input and scratch files
Set Efile “MixMake.Job”
Set Cfile “MacC.Files”
Set Lfile “CLink.Tmp”
SetTfile “TLink.Tmp”
Set Seqfile “Sequencer.R”
Delete -i “{Efile}” “{Cfile}” “{Lfile}” “{Tfile}”
Echo “#include ”Types.r””>”{Seqfile}”
Echo “include ”{MPW}Applications:Sequencer” ’CODE’ ;”
 >>”{Seqfile}”
Echo “resource ‘STR#’ (128,”MixMake Compile commands”) {“>>”{Seqfile}”
Echo -n “{ “>>”{Seqfile}”
#Application run flags
Set RunMacC 0
Set RunLink 0  # Consulair
Set RunTMLLink 0
#Make’s working directory
Set MakeWD “`Directory`”
Export MakeWD

### Run the Make tool
make {“Parameters”} > MakeOutApps  Dev:StdOut

### Construct the input for the Exec application
Execute MakeOutApps
Delete MakeOutApps
If ({RunMacC} == 1)
 Echo “C{Cfile}  ExecDone”>>”{Efile}”
End
If ({RunLink} == 1)
 Catenate “{Lfile}” >>”{Efile}”
 Delete -i “{Lfile}”
End
If ({RunTMLLink} == 1)
 Catenate “{Tfile}” >>”{Seqfile}”
 Delete -i “{Tfile}”
End
If ({RunMacC} == 1 || {RunLink} == 1)
 Echo “”{MakeWD}{Efile}”;”>>”{Seqfile}”
 Echo “”{MPW}Applications:Exec”;”>>”{Seqfile}”
End
Open -t “{Seqfile}”
Find   “{Seqfile}”
Clear \;\ “{Seqfile}”
Echo “n}n};”>>”{Seqfile}”
Close -y “{Seqfile}”
Rez -o “{MPW}Applications:PasRunner” “{Seqfile}”
If {Status} == 0
 Delete -i “{Seqfile}”
End
Unset RunMacC
Unset RunLink
Unset RunTMLLink
Unset Lfile
Unset Cfile
Unset Tfile
Unset MakeWD
Unset Seqfile

### Set up the Resume tasks for after the applications
### Primarily for error handling
Set PostApp “{MPW}UserResume”
Echo “Unset Efile” >”{MPW}UserResume”
Catenate  “{MPW}MakeSupport:MixMakeResume” >>”{MPW}UserResume”

### If Diagnostic menu item is still around from
###an earlier build cycle, delete it.
Set __oldExit {Exit}
Set Exit 0
DeleteMenu File “Open Diagnostics ”  Dev:Null
Set Exit {__oldExit}
Unset __oldExit

### Run applications using the custom Sequencer
“{MPW}Applications:PasRunner”
########################################

###File MixMake.Template.1
###MixMake.Template.1
### Sample makefile for version 1 MixMake Command
###W.G. Powell 1987for MacTutor

### Make variable definitions
RezOpt = “-t ‘RSRC’”

### Directory dependency rules go here 
# This one specifies that rels are
# in sister directory to that of the sources
::Rels: ƒ :
# Can add others of form 
# {AltSrcDir}:Rels: ƒ {AltSrcDir}

### Default dependency rules for compilers
# Default C compilation rule
.Rel  ƒ .c
 Delete -i {TargDir}{Default}.Rel
 Echo {DepDir}{Default}.c >> “{Cfile}”
 Set RunMacC 1
# Default assembly rule
.Rel  ƒ .Asm
 Delete -i {TargDir}{Default}.Rel
 echo {DepDir}{Default}.Asm >> “{Cfile}”
 Set RunMacC 1
# Default Pascal compilation rule
.Rel  ƒ .Pas
 Delete -i {TargDir}{Default}.Rel
 echo “Pascal  {DepDir}{Default}.Pas Exec    Exec”
 >> “{Efile}”
# Default resource compilation rule
.Rsrc ƒ .R
 Delete -i {TargDir}{Default}.Rsrc
 Rez {RezOpt} -o {TargDir}{Default}.Rsrc {DepDir}{Default}.R

### Program-specific dependency rules go here
### For a program, should explicitly list the 
### link script (.Link file), all objects (.Rel files),
###and all resource files (.RSRC) needed to build.
# Sample dependency rule for Consulair linker
#  prog1  ƒ  prog1.Link prog1.Rel sub1.rel prog.Rsrc
#Echo “Link prog1.Link  Exec  Done”>>”{Lfile}”
#Set RunLink 1
#
# Sample dependency rule for TML Linker
#prog2  ƒ  prog2.Link  prog2.rel  lib.rel prog.RSRC
#Echo “TML Link  prog2.Link  Exec  Done”>>”{Lfile}”
#Set RunTMLLink 1
 
################################################

###File MixMake.Template.2
###MixMake.Template.2
### Sample makefile for version 2 MixMake Command
###W.G. Powell 1987for MacTutor

### Make variable definitions
RezOpt = “-t ‘RSRC’”

### Directory dependency rules go here 
# This one specifies that rels are
# in sister directory to that of the sources
::Rels: ƒ :
# Can add others of form 
# {AltSrcDir}:Rels: ƒ {AltSrcDir}

### Default dependency rules for compilers
# Default C compilation rule
.Rel  ƒ .c
 Delete -i {TargDir}{Default}.rel
 Echo {DepDir}{Default}.c >> “{Cfile}”
 Set RunMacC 1
# Default assembly rule
.Rel  ƒ .Asm
 Delete -i {TargDir}{Default}.Rel
 echo {DepDir}{Default}.Asm >> “{Cfile}”
 Set RunMacC 1
# Default Pascal compilation rule
.Rel  ƒ .Pas
 Delete -i {TargDir}{Default}.Rel
 Echo “”{DepDir}{Default}.Pas”>>”{Seqfile}”
 Echo “”{MPW}Applications:Pascal”>>”{Seqfile}”
# Default resource compilation rule
.Rsrc ƒ .R
 Delete -i {TargDir}{Default}.Rsrc
 Rez {RezOpt} -o {TargDir}{Default}.Rsrc  
 {DepDir}{Default}.R

### Program-specific dependency rules go here
### For a program, should explicitly list the 
### link script (.Link file), all objects (.Rel files),
###and all resource files (.RSRC) needed to build.
# Sample dependency rule for Consulair linker
#  prog1  ƒ  prog1.Link prog1.Rel sub1.rel prog.Rsrc
#Echo “Link prog1.Link  Exec  Done”>>”{Lfile}”
#Set RunLink 1
#
# Sample dependency rule for TML Linker
#  prog2  ƒ  prog2.Link  prog2.rel  lib.rel prog.RSRC
#Echo “”{full directory}prog2.Link”;”>>”{Seqfile}”
#Echo “”{MPW}Applications:TML Link”;”>>”{Seqfile}”
#Set RunTMLLink 1
 
############################################

###File Done.c
 
############################################

###File Done.c
 /**************************************************/
 /*     Done.c   */
 /*Bogus Application for Exec to use to            */
 /*Return to MPW shell    */
 /*Written for Consulair MacC */
 /**************************************************/
#Options E G H 
main ()
{
void exit() ; 
exit (0) ; 
}
###############################################

###     File MixMakeResume
# MakeResume -- a command file to be executed when
#  resuming from applications such as MacC 
#W. PowellMay 1987 for MacTutor

# Don’t terminate command file on non-zero status
Set __oldExit {Exit}
Set Exit 0
Set CompErrDir “`Directory`”
Export CompErrDir
# Establish a menu item for error handling
DeleteMenu File “Open Diagnostics ”   Dev:Null
AddMenu File “Open Diagnostics /E” ‘Set Exit 0; 
Export MakeErr ; Set __origDir “`Directory`” ; 
Directory {CompErrDir} ;
CompErr ;
Set MakeErr {Status} ; Directory {__origDir} ;
Unset __origDir’
# {MakeErr} is a flag used by CompErr, the command
#file which opens compiler error files
Set MakeErr 0
Export MakeErr
# Create summary files listing all files containing
#  current compiler or linker diagnostics
echo  ‘’ > TMLP.Fer
echo  ‘’ > Link.Fer
Files  .Pas >Dev:Null  Dev:Null
If ( {Status} == 0 )
 For __fil In  .Pas “”
 If (“{__fil}” != “”)
 Make {__fil} -f “{MPW}”MakeSupport:MakePErrs.mk 
 End
 End > MakeOutApps   Dev:Null
End
Files  .Link >Dev:Null  Dev:Null
If ( {Status} == 0 )
 For __fil In  .Link “”
 If (“{__fil}” != “”)
 Make {__fil} -f “{MPW}”MakeSupport:MakePErrs.mk 
 End
 End >> MakeOutApps   Dev:Null
End
Unset __fil
Files MakeOutApps >Dev:Null  Dev:Null
If ( {Status} == 0 )
 Execute MakeOutApps
 rm MakeOutApps
End
# Move insertion point to front of each 
#summary file window and save window 
#with that selection
Begin
 Open -t MACC.Fer 
 Find • MACC.Fer 
 Replace § ‘ ‘ MACC.Fer
 Save MACC.Fer 
 Close MACC.Fer
 Open -t TMLP.Fer 
 Find • TMLP.Fer
 Replace § ‘ ‘ TMLP.Fer  
 Save TMLP.Fer
 Close TMLP.Fer
 Open -t Link.Fer 
 Find • Link.Fer 
 Replace § ‘ ‘ Link.Fer
 Save Link.Fer
 Close Link.Fer
End  Dev:Null
# Reset {Exit} variable to previous value
Set Exit {__oldExit}
Unset __oldExit
#################################################

###File MakePErrs.mk
# Make file to create error summary files from 
# build cycles with TML Pascal and Consulair and TML Linkers
# This makefile is used by MixMake command’s Resume tasks
#  (see file MixMakeResume)
#W. Powell1987 for MacTutor

#These dependency rules ensure we get only 
#source files which
#have NOT been changed since the error files 
#were written.
.Pas  ƒ .PErr  # for TML Pascal
 echo {DepDir}{Default}.Pas{DepDir}{Default}.PErr 
 >> TMLP.Fer
.Link ƒ .LErr  # for Consulair and TML Link
 echo {DepDir}{Default}.Link {DepDir}{Default}.LErr 
 >>Link.Fer
#################################################

###File CompErr

### Shell script to find and open error files 
### generated by MixMake 
### command, opening a different files in sequence 
### on subsequent
### calls.  “.Fer” files should have been created by MixMake’s 
### Resume tasks.  This script MUST be run by the menu item 
###provided by the Resume task to work properly.
###    {Status} returned by this command on exit is the new
###value for global shell variable {MakeErr}.
###W.G. Powell 1987for MacTutor

# Some commands intentionally return nonzero status, so
# don’t terminate this command file
Set _oldExit {Exit}
Set Exit 0
###Part 1
### Process MacC error files
Files MACC.Fer >Dev:Null  Dev:Null # Don’t need output
If ({Status} == 0 && {MakeErr} == 0 )
# File MACC.Fer exists   AND still processing Mac C errors.
 Open -t MACC.Fer
 Find /’Errors in File: ‘/:/’ (‘/ MACC.Fer  
 # Open bad source
 If ({Status} == 0)
 # find directory of source file
 Set __tmpSrcDir “`Catenate MACC.Fer.§`”
 Open -t -n MACC.tmp.tmp  Dev:Null
 Echo -n “{__tmpSrcDir}”>MACC.tmp.tmp  Dev:Null
 Find   MACC.tmp.tmp  Dev:Null
 Clear \:\:  MACC.tmp.tmp  Dev:Null
 Find •:  MACC.tmp.tmp  Dev:Null
 Set __tmpSrcDir “`Catenate MACC.tmp.tmp.§`”  Dev:Null
 Close -n MACC.tmp.tmp
 # open the file
 Open  “`Catenate MACC.Fer.§`”
 If ({Status}   0)
 Alert “Cannot open source file `Catenate MACC.Fer.§`”
 End
 Find /’(See ‘/:/’)’/ MACC.Fer # Open diagnostic file
 If ({Status} == 0)
 Open “`Catenate MACC.Fer.§`”  Dev:Null
 If ({Status}   0)
 # try again in source directory
 Open “{__tmpSrcDir}`Catenate MACC.Fer.§`”
 If ({Status}   0)
 Alert “Cannot open error file `Catenate MACC.Fer.§`”
 End
 Unset __tmpSrcDir
 End
 Else
 Alert “Don’t know corresponding error file”
 End
 Else # Open file MACC.Fer after processing all 
 # the files it named
 Open MACC.Fer
 If ({Status}   0)
 Alert “Cannot open error file MACC.Fer”
 End
Alert “Finished last Consulair C Error File.nTML Pascal Next”
 # Save current selection
 Find § MACC.Fer  Dev:Null
 Replace § “ “ MACC.Fer  Dev:Null
 Save MACC.Fer  Dev:Null
 Close -y MACC.Fer  Dev:Null
 Exit ‘Evaluate {MakeErr} + 1‘
 End
 # Save current selection
 Find § MACC.Fer  Dev:Null
 Replace § “ “ MACC.Fer  Dev:Null
 Save MACC.Fer  Dev:Null
 Close -y MACC.Fer  Dev:Null
 Exit {MakeErr}
Else If ({MakeErr} == 0)
 Set MakeErr ‘Evaluate {MakeErr} + 1‘
 # Go on to Pascal files
End
###Part 2
### Process TML Pascal Error files
Files TMLP.Fer >>Dev:Null  Dev:Null
If ({Status} == 0 && {MakeErr} == 1)
 Open -t TMLP.Fer
 Find /•/:/ / TMLP.Fer
 If ({Status} == 0)
 Open “`Catenate TMLP.Fer.§`” # Open bad source
 If ({Status} != 0)
 Alert “Cannot open source file `Catenate TMLP.Fer`”
 End
 Find //:/ / TMLP.Fer
 If ({Status} == 0)
 Open “`Catenate TMLP.Fer.§`” # Open diagnostic file
 If ({Status} != 0) 
 Alert “Cannot open error file `Catenate TMLP.Fer.§`”
 End
 Else
 Alert “No corresponding error file”
 End
 Else
Alert “Finished last TML Pascal Error File.nLink diagns next.”
 # Save current selection
 Find § TMLP.Fer  Dev:Null
 Replace § “ “ TMLP.Fer  Dev:Null
 Save TMLP.Fer  Dev:Null
 Close -y TMLP.Fer  Dev:Null
 Exit ‘Evaluate {MakeErr} + 1 ‘
 End
 # Save current selection
 Find § TMLP.Fer  Dev:Null
 Replace § “ “ TMLP.Fer  Dev:Null
 Save TMLP.Fer  Dev:Null
 Close -y TMLP.Fer  Dev:Null
 Exit {MakeErr}
Else If ({MakeErr} == 1) 
 Set MakeErr ‘Evaluate {MakeErr} + 1‘
 # Go on to linker errors
End
###Part 3
### Process Linker Error files
Files Link.Fer >>Dev:Null  Dev:Null
If ({Status} == 0 && {MakeErr} == 2)
 Open -t Link.Fer
 Find /•/:/ / Link.Fer
 If ({Status} == 0)
 Open “`Catenate Link.Fer.§`”   # Open bad source
 If ({Status} != 0)
 Alert “Cannot open link script `Catenate TMLP.Fer`”
 End
 Find //:/ / Link.Fer
 If ({Status} == 0)
 Open “`Catenate Link.Fer.§`”      # Open diagn files
 If ({Status} != 0) 
 Alert “Cannot open error file `Catenate TMLP.Fer.§`”
 End
 End
 Else
 Alert “All error files have now been processed!”
 # Save current selection
 Find § Link.Fer  Dev:Null
 Replace § “ “ Link.Fer  Dev:Null
 Save Link.Fer  Dev:Null
 Close -y Link.Fer  Dev:Null
 DeleteMenu File “Open Diagnostics ”
 Exit ‘Evaluate {MakeErr} + 1 ‘
 End
 Exit {MakeErr}
Else If ({MakeErr} == 2)
 Alert “All error files have now been processed!”
 DeleteMenu File “Open Diagnostics ”
 Exit ‘Evaluate {MakeErr} + 1‘
End
Exit {MakeErr} 
#############################################

###File QuitPermVars
####  This shell script is run by the Quit script
####  to save any shell variables which must be
####  preserved through a Quit
####  W.G. Powell 1987 for MacTutor

 #Clear the file
 Echo “” > “{MPW}StartPermVars”
 
 # Save CompErr (Diagnostic viewer) state variables
Echo “# State vars for CompErr” >>”{MPW}StartPermVars”
 Set MakeErr >> “{MPW}StartPermVars”
 Echo “Export MakeErr” >> “{MPW}StartPermVars”
 Set CompErrDir >>”{MPW}StartPermVars”
 Echo “Export CompErrDir” >> “{MPW}StartPermVars”
 If ({MakeErr} < 3) 
 # Still some compiler errors to process?
 AddMenu File “Open Diagnostics ” 
 >> “{MPW}StartPermVars”
 End
 
################################################

###File StartPermVars
#initial default values for CompErr state variables
Set MakeErr 3
Export MakeErr
Set CompErrDir “”
Export CompErrDir
################################################

###File Man
###MPW command “man” for on-line help
###Written 1987 W.G. Powell
###for MacTutor
###Search more than one file sequentially for help text

Set Exit 0
Set Reval 0
###   Get standard command names from alias dictionary
# need temporary file
Set _dmfl_ dumrxfx_
#Unalias the input arguments
Echo {Parameters} | 
  Canon “{MPW}billTools:Alias.Dict” > “{_dmfl_}”
#Set new input arguments
Set HelpList “`Catenate {_dmfl_}`”
Delete -n {_dmfl_}
###Look for each requested item individually
For HelpItem In {HelpList}
 # Look in default help file first
 Help {HelpItem}   Dev:Null
 Set Retval {Status}
 If ({Retval} == 2 || {Retval} == 1)
 # Look in local help file if not in default
 Help -f “{MPW}Local.Help” {HelpItem}   Dev:Null
 Set Retval {Status}
 End
 If ({Retval} != 0)
 # Keep a list of items NOT found for diagnostic
 Set Reval {Retval}
 Set NotFnd “{NotFnd} {HelpItem}”
 End
End
###Send error message if items not found.
If ({Reval} != 0)
 Echo “### Help:  Cannot find items:”>Dev:StdErr
 Echo “###{NotFnd}”>Dev:StdErr
End
Unset NotFnd
Unset _dmfl_
Unset HelpItem
Unset HelpList
Unset Retval
Exit {Reval}
################################################

###File Local.Help
Help files for locally developed commands
W.G. Powell 1987 for MacTutor

-
Local - MPW Local Custom Commands
 Get info on specific command by entering
 Man CommandName

CompErr # Open compiler diagnostics and erroneous 
 #sources
MacC    # Consulair applications: compiler, linker, etc.
MakeCPath # Use shell variables to create Consulair paths
MakePPath # Use shell variables to make TML paths
Man# “help” utility including local help files
MixMake # MPW make with Mac C, TML systems
NullFile# A “do nothing” command file
TML# TML Pascal compiler and linker
UpdatePaths # Update TML Pascal, Mac C search paths

-
Man [(CommandName | Keyword)   ]
 Abbreviated on-line manual entries for commands.  This
 is an elaborate version of MPW help, which searches 
 several files for help info.
-
UpdatePaths
 Create new search paths for Consulair and TML, but only
1) If {MPW}Startup (which contains path variables) changes
2) If {MacCLib}C.Path.Template (Path Manager input) changes
 SEE ALSO:  MakePPath, MakeCPath
-
MakePPath
 Create new “Paths File” for TML Pascal in system folder.
 Pathnames come from shell variables defined in Startup:
 {TMLPasLib} contains one directory for libraries
 {TMLPasInc} contains up to 4 directory  pathnames
 separated by commas.
 User must select Set Paths DA 
 and click “OK” afterwards.
-
MakeCPath
 Create new “Paths.Rsrc” for Consulair in system folder.
 Also writes equivalent Path Manager text input file in 
 system folder as “MacC.Path”.  Input text file uses
 {MacCLib}C.Path.Template as main form, with additional
 pathnames from shell variables defined in Startup file:
 {MacCLib} contains one directory pathname for libraries.
 C.Path.Template names several paths relative to 
 {MacCLib}.
-
MixMake [option ] target   
 Build programs using MacC and TML compilers 
 Options:
      -d name[=value]# define variable name as value 
   #  (override definitions in makefile)
      -e                  # rebuild everything regardless of dates
      -f makefile         # read dependencies from makefile --
   #  (default “MakeFile”)
 Other standard Make options are not supported.  
 The following options
 are helpful using the standard Make tool with the
 MixMake input dependency file
    -r  # write roots of dependency graph to output
    -s  # write structure of target dependencies to output
    -t  # touch dates of targets and prerequisities
    -u  # identify targets in makefile not reached in build
    -v  # write verbose explanations to diagnostics
 SEE ALSO: Make, CompErr
-
CompErr 
(ALWAYS access via item “Open Diagnostics ” in File menu)
 Opens MacC, TML Pascal, or Linker diagnostic
 and source files.  Generally only will work following a build
 attempt using command “MixMake”.  
 Opens only one pair of files on each invocation 
 - must be called repeatly for each source.
-
NullFile
 Contains no command text.  Does nothing.
-
######################################
 File  Alias.Dict
File  Target
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Whitethorn Games combines two completely...
If you have ever gone fishing then you know that it is a lesson in patience, sitting around waiting for a bite that may never come. Well, that's because you have been doing it wrong, since as Whitehorn Games now demonstrates in new release Skate... | Read more »
Call of Duty Warzone is a Waiting Simula...
It's always fun when a splashy multiplayer game comes to mobile because they are few and far between, so I was excited to see the notification about Call of Duty: Warzone Mobile (finally) launching last week and wanted to try it out. As someone who... | Read more »
Albion Online introduces some massive ne...
Sandbox Interactive has announced an upcoming update to its flagship MMORPG Albion Online, containing massive updates to its existing guild Vs guild systems. Someone clearly rewatched the Helms Deep battle in Lord of the Rings and spent the next... | Read more »
Chucklefish announces launch date of the...
Chucklefish, the indie London-based team we probably all know from developing Terraria or their stint publishing Stardew Valley, has revealed the mobile release date for roguelike deck-builder Wildfrost. Developed by Gaziter and Deadpan Games, the... | Read more »
Netmarble opens pre-registration for act...
It has been close to three years since Netmarble announced they would be adapting the smash series Solo Leveling into a video game, and at last, they have announced the opening of pre-orders for Solo Leveling: Arise. [Read more] | Read more »
PUBG Mobile celebrates sixth anniversary...
For the past six years, PUBG Mobile has been one of the most popular shooters you can play in the palm of your hand, and Krafton is celebrating this milestone and many years of ups by teaming up with hit music man JVKE to create a special song for... | Read more »
ASTRA: Knights of Veda refuse to pump th...
In perhaps the most recent example of being incredibly eager, ASTRA: Knights of Veda has dropped its second collaboration with South Korean boyband Seventeen, named so as it consists of exactly thirteen members and a video collaboration with Lee... | Read more »
Collect all your cats and caterpillars a...
If you are growing tired of trying to build a town with your phone by using it as a tiny, ineffectual shover then fear no longer, as Independent Arts Software has announced the upcoming release of Construction Simulator 4, from the critically... | Read more »
Backbone complete its lineup of 2nd Gene...
With all the ports of big AAA games that have been coming to mobile, it is becoming more convenient than ever to own a good controller, and to help with this Backbone has announced the completion of their 2nd generation product lineup with their... | Read more »
Zenless Zone Zero opens entries for its...
miHoYo, aka HoYoverse, has become such a big name in mobile gaming that it's hard to believe that arguably their flagship title, Genshin Impact, is only three and a half years old. Now, they continue the road to the next title in their world, with... | Read more »

Price Scanner via MacPrices.net

B&H has Apple’s 13-inch M2 MacBook Airs o...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for up to $150 off Apple’s new MSRP, starting at only $849. Free 1-2 day delivery is available to most US... Read more
M2 Mac minis on sale for $100-$200 off MSRP,...
B&H Photo has Apple’s M2-powered Mac minis back in stock and on sale today for $100-$200 off MSRP. Free 1-2 day shipping is available for most US addresses: – Mac mini M2/256GB SSD: $499, save $... Read more
Mac Studios with M2 Max and M2 Ultra CPUs on...
B&H Photo has standard-configuration Mac Studios with Apple’s M2 Max & Ultra CPUs in stock today and on Easter sale for $200 off MSRP. Their prices are the lowest available for these models... Read more
Deal Alert! B&H Photo has Apple’s 14-inch...
B&H Photo has new Gray and Black 14″ M3, M3 Pro, and M3 Max MacBook Pros on sale for $200-$300 off MSRP, starting at only $1399. B&H offers free 1-2 day delivery to most US addresses: – 14″ 8... Read more
Department Of Justice Sets Sights On Apple In...
NEWS – The ball has finally dropped on the big Apple. The ball (metaphorically speaking) — an antitrust lawsuit filed in the U.S. on March 21 by the Department of Justice (DOJ) — came down following... Read more
New 13-inch M3 MacBook Air on sale for $999,...
Amazon has Apple’s new 13″ M3 MacBook Air on sale for $100 off MSRP for the first time, now just $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD/Space Gray): $999 $100 off MSRP... Read more
Amazon has Apple’s 9th-generation WiFi iPads...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80-$100 off MSRP, starting only $249. Their prices are the lowest available for new iPads anywhere: – 10″ 64GB WiFi iPad (Space Gray or... Read more
Discounted 14-inch M3 MacBook Pros with 16GB...
Apple retailer Expercom has 14″ MacBook Pros with M3 CPUs and 16GB of standard memory discounted by up to $120 off Apple’s MSRP: – 14″ M3 MacBook Pro (16GB RAM/256GB SSD): $1691.06 $108 off MSRP – 14... Read more
Clearance 15-inch M2 MacBook Airs on sale for...
B&H Photo has Apple’s 15″ MacBook Airs with M2 CPUs (8GB RAM/256GB SSD) in stock today and on clearance sale for $999 in all four colors. Free 1-2 delivery is available to most US addresses.... Read more
Clearance 13-inch M1 MacBook Airs drop to onl...
B&H has Apple’s base 13″ M1 MacBook Air (Space Gray, Silver, & Gold) in stock and on clearance sale today for $300 off MSRP, only $699. Free 1-2 day shipping is available to most addresses in... Read more

Jobs Board

Medical Assistant - Surgical Oncology- *Apple...
Medical Assistant - Surgical Oncology- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple 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
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Business Analyst | *Apple* Pay - Banco Popu...
Business Analyst | Apple PayApply now " Apply now + Apply Now + Start applying with LinkedIn Start + Please wait Date:Mar 19, 2024 Location: San Juan-Cupey, PR Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.