TweetFollow Us on Twitter

Prograph Primitive
Volume Number:7
Issue Number:5
Column Tag:Programmers' Workshop

Prograph Primitives

By Terry Kilshaw, British Columbia, Canada

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

[Terry Kilshaw is a free-lance consultant, writer and programmer now living in the Okanagan valley of British Columbia. He has been designing software and programming all sorts of computers for 12 years, the last 3 1/2 of which he spent as Manager of Software Development for TGS Systems. He can be reached at (604) 762 4451.]

Adding C Primitives to Prograph

Prograph is a very high-level, object-oriented, pictorial, data-flow programming language and integrated development environment. For an introduction to Prograph see Prograph Raises OOP to New Height MacTutor, July 1989 by Mark Szpakowski. It is fairly straightforward to add your own primitives to Prograph. These can be written in such a way that they can be called from both interpreted and compiled code.

In this article I’ll describe a couple of useful primitives, written using THINK C. One is for creating a sorted list of unique strings from an unsorted list whose strings are not necessarily unique, and the other is for performing a binary search on a sorted list. On the way we will take a look at some practical details of Prograph primitive construction and finish with a detailed example of a simple Prograph program which shows a useful string permutation generator and provides a test bed for the two new primitives.

Readers who don’t use or intend to use Prograph will still be able to adapt the C language binary search for their own purposes.

The Structure of Prograph Data Items

All Prograph data items are referred to by Macintosh Handles and begin with the three data fields, type, save and use. For historical reasons the size of the save and use fields, and indeed of all index fields in interpreted code, is an unsigned two byte integer value. In compiled code, however, they are four byte unsigned integers. Index fields include the maximum length of a string and the maximum number of elements in a list. Because Handles are used for strings and lists they can be grown and shrunk dynamically, a fact that we will make use of later. The reason for the difference in data type definitions lies in the evolution of Prograph. In subsequent releases we can expect these differences to disappear.

After the type, save and use, in fixed length data items, comes the actual data: an integer, floating point or boolean value. Lists, strings and instances of classes can vary in length and therefore also have a length field.

The Primitives

The two primitives which will be created are called binlist-sort and binlist-find. A “binlist” is just a name for a sorted list of strings with no duplicates. These primitives would appear in Prograph as follows:

binlist-sort expects a list of strings on its input. It discards any duplicate entries and creates the sorted list as its output.

binlist-find expects a sorted list of strings, with no duplicates, on its first input and the string to search for as its second input. binlist-find returns a boolean, on its first output, indicating TRUE if the string was found in the list and, on its second output, the list index where the string was found. If the string was not found, the value of the first output will be FALSE and the second output will be the list index where the string could be inserted into the binlist. So binlist-find can be used both for finding an entry in a binlist and for maintaining new entries to the list.

Making Primitives for the Interpreter

The _INTERPRETER flag in X_includes.h (see below) should be set to 1. Each group of primitives is compiled into a code resource whose resource type is defined as TGSC. For each primitive in the code resource an STR# resource consisting of 3 strings is created, giving the mnemonic names of input and output arguments, the types of input and output arguments, and a description of what the primitive does. The resource name is used as the primitive name. The TGSC and STR# resources are placed together in a file whose creator type is TGSL and whose file type is TGSC. On startup Prograph automatically searches its own folder for files of type TGSC, the code resources and the primitive names are loaded into the interpreter, and the primitives are available for use.

The STR# resources are shown, in RMaker format, in the listing of binlist_prims_I.r at the end of the article. Obviously you can use ResEdit or whatever resource creation tools are available to you. After running through RMaker an output file called binlist_prims.p.rsrc is produced. If this file is placed in the same folder as your THINK C project then the STR# resources will automatically be copied into the code resource file when the Build Code Resource item of the Project menu is chosen. The name of the file containing the finished primitives is binlist_prims.

The name of the STR# resource is used as the primitive’s name in Prograph. The STR# resource’s number maps on to the first argument of the AddPrimitive call in the main function as described below. The (32) which follows the STR# number indicates that the resource is to be purgeable. The three strings which follow that are for display by the Primitives item of Prograph’s Info menu.

Making Primitives for the Compiler

The _INTERPRETER flag in X_includes.h (see below) should be set to 0. In this case no STR# resources are needed, since on-line help is only appropriate in the interpreter. The primitives are then compiled and built into a THINK C library. That library is then added to your Prograph project and linked in with the Prograph code and libraries to form the finished stand-alone program.

Naming Conventions

Primitives can be named any way you like, but there is an important convention for the names of the C language functions which those primitives call which must be used if you want the compiler to recognize them. A primitive can be thought of as a sort of Universal Method. In compiled code, primitives and Universal Methods are actually indistinguishable. Consequently the naming conventions, for a primitive, start from the name that it is called by in the interpreter and prefix it with the characters U_. For example the primitive test would become U_test. The naming conventions of the C language, however, allow only alpha-numeric characters and the underscore is legal function names. In order to cope with names which include special characters such as the hyphen, the plus sign and so on, each special character is replaced with its two digit hexadecimal ASCII value and preceded and followed by underscores. For example binlist-sort becomes U_binlist_2D_sort, because 2D is the ASCII equivalent for the hyphen ( or minus sign, if you will ) in hex. form.

Now to the Code

The source for the C language examples can be found in the file binlist_prims.c

MacTypes.h is a standard header provided by THINK C. X_includes.h is supplied as part of the Prograph product and provides type declarations for code called by both the interpreter and the compiler. This file also defines the value of the preprocessor symbol _INTERPRETER, with the possible values of 0 for FALSE and 1 for TRUE. The value of _INTERPRETER is then used throughout the primitive’s code, for preprocessor directives to allow the single source to be used for primitives which will work in interpreted and compiled Prograph programs.

X_includes.h also provides typedefs for all of the usual C types, which map as follows:

THINK C Type Prograph Typedef

char Int1

unsigned char Nat1

int Int2, Bool

unsigned int Nat2

long Int4

unsigned long Nat4

Bool can have only two values, 0 & 1, defined as FALSE and TRUE respectively.

The data item typdefs all have a similar form. Here, for example is the one for Prograph’s integer data item as used by the interpreter:

/* 1 */

 typedef struct
 Int2 type;
 Nat2 save;
 Nat2 use;
 Int4 value;
 } *C_integer;

Now when an integer is declared in the code its declaration is of the form:

/* 2 */

 C_integer *myC_integer;

Experienced C programmers may wonder, when the data item is going to be referenced via a handle anyway, why the extra level of indirection is not included in the typedef. The reason for this is to try and make it easier for programmers to utilize THINK C’s object oriented syntax if it becomes necessary to use code originally written as a primitive directly as a class or universal method in compiled Prograph programs. See the Writing XCode section of Appendix II in the Prograph Reference Manual for more details.

An Index type is defined for separate use in interpreted and compiled Prograph programs, as explained above. LT, EQ and GT are equates for the return value of compare_strings.

Prototypes for all of the functions defined in the file (except for main) allow for forward references in the code and for the Check Pointer Types and Require Prototypes options to be enforced.

ct, which stands for common table, is a pointer to an array of ProcPtrs. main, which is called only once, when Prograph is launched, sets up this pointer to the common table. This allows primitives to have access to a group of functions which simplify the creation of data items, the manipulation of lists and instances, the maintenance of reference counts and so on. A number of these functions will be used in the construction of our two primitives.

The AddPrimitive functions, add the two primitives to an internal Prograph table. From that point on they can be used just like any of the primitives which are supplied with Prograph.

In a THINK C code-resource, global and static variables are referenced off of register A4. When each code resource is loaded and each call to AddPrimitive is made, the value of A4 is saved in a table. Whenever a call to that primitive is made later, Prograph automatically sets up the A4 register using the value which was saved. The consequence is that the writer of primitives can always assume that it is ok to use global variables in his or her code resources.

This whole section is bracketed by #if _INTERPRETER directives. It is only needed to add your primitives to the interpreter’s tables and to allow you access to the Supplied Functions. In compiled Prograph code the Supplied Functions are accessed through the Prograph library and the primitives are called just like any other function.

U_binlist_2D_sort( )

The first thing done in any primitive is to check the arity of the call, i.e. the number of inputs and outputs it has. Since binlist-sort is a fixed arity primitive (i.e. it does not have a variable number of inputs or outputs) the arity check is very simple. If the primitive is not being called with precisely one input and one output then an arity error is returned immediately. In THINK C the macro GETARITY extracts its values from register A0.

The supplied function IsType evaluates to TRUE or FALSE. It is passed the data item to be checked and the type identifier to check it against. If this input is not a list a type error is returned with the number of the input, where the offending type was found, added to the error value.

When called from a compiled Prograph program no primitive should ever fail. All error conditions should be discovered and programmed for using the interpreted environment. Consequently all error checking code can be dispensed with in the compiler version. Hence it is bracketed here by the #if _INTERPRETER ... #endif directives.

Prograph employs a use or reference counting mechanism as the basis of its automatic garbage collection. This scheme requires that a field in a data item is used to keep a count of the number of references to it. Whenever a new reference is made to an item, its use count is incremented by one. When that reference ceases, the use count is decremented by one. When an item’s use count reaches zero the item is garbaged. In Prograph a data item can be referenced from only a limited number of places. They are as follows:

1) from a persistent

2) rom a class attribute

3) from an instance attribute

4) from a list element

5) from a constant operation

6) from a match operation

7) from the root of an operation after it has been executed and before its case has finished execution.

To properly maintain use counts in a Prograph primitive you need to ask the question, “After the primitive has finished execution, what references to data items will remain?” Categories 3, 4 and 7 are the only ones that can be accessed from inside a primitive. You need also to realize that because of the data-flow nature of Prograph, with one exception, data items which are input to primitives are never altered. Once created they are basically constant values.

The exception referred to above is that of instances of classes. Such instances do have their data values changed, or rather exchanged. For example the first attribute of an instance may be an integer with the value 4. A new integer, with the value of 33 may be assigned to that attribute thereby changing the instance. But the value of the first integer itself will not be changed. Primitive data values, and these include the list type, are only created or destroyed, never changed.

Here then, it is necessary to create a new list, which is done using the supplied function MakeC_list. The new list is automatically created with a use count of 1, and we specify a length of 0. Unless any input values of the wrong type are encountered, this new list will be assigned to the output root of the primitive. This will be the only continuing reference to that list after the primitive has finished executing. A use count of 1 means “one thing is pointing to me”, the root of the primitive.

Next the type of each data item in the input list is checked. If it is not a string then the newly created list is garbaged by passing it to DecUse and a type error is returned, specifying input 1 as the origin of the error. When passed a list, the supplied function DecUse first decrements the use count of the list. If that use count is 0 then the use count of the data item in each list slot is also decremented. If the use count of any item falls to 0 its handle is garbaged i.e. its storage is released back to the heap. As before, this section of code is redundant in the compiler version of the primitive.

If the data item is a string we check to see if it is already in the new list by making a call to the function binlist_find. The binlist_find routine does a binary search on the contents of the new list, to see if a duplicate string is already in that list and to return the index for insertion if it is not.

If not found, the string is inserted in the list at the given index using the supplied function ListInsertSlot. The third argument to ListInsertSlot indicates that the use count of the inserted string should be incremented by 1. We know that this string is already being pointed to from InList and it will now also be referenced from sortList. Both of these references will continue after the primitive finishes executing.

This is why, if the primitive is aborted as described above, it is necessary to pass sortList to DecUse, so that the incremented use counts in each of the strings can be properly decremented before sortlist is garbaged.

Lastly the new list is assigned to the output and PCF_TRUE is returned as the value of the primitive’s control state. PCF stands for Prograph Control Flag. A primitive which encounters no errors during execution may return a value of TRUE or FALSE. FALSE is valid only for boolean primitives, such as integer?, which have no outputs and whose value can be TRUE or FALSE.

There is one obvious inefficiency in the binlist-sort routine. For every string inserted it is necessary to expand the list handle by 4 bytes, which may cause the whole handle contents to be copied, if it needs to be moved for the grow to occur. And, if the insertion is not at the very end of the list, all list entries from the index to the end of the list will need to be moved to make space for the new list element. On the other hand it is only the 4 byte references to the strings which are moved and not the strings themselves. In practice performance has been quite acceptable as you will see in the Prograph test example.

U_binlist_2D_find( )

We begin, as usual, by checking the arity and the types of inputs, i.e. 2 inputs ( a list and a string ) and 2 outputs. No attempt is made here to verify that the input list contains only strings and no other types of data item or NULL slots. Nor is there any check to ensure that the list is in sorted order. It is up to the user to maintain his or her binlists properly. As before, the arity and type checks are redundant in the compiler version and are bracketed by preprocessor directives.

The guts of the find mechanism has been factored out into the function binlist_find, so that it can be called from U_binlist_2D_sort as well as from U_binlist_2D_find. binlist_find is sent the list and the string to search for. It returns TRUE or FALSE in found and the index at which it was found, or at which the string could be inserted into the list, in index.

The values of found and index are then packaged up as a boolean and an integer which are created and assigned to outputs 1 and 2, respectively, using the supplied functions MakeC_boolean and MakeC_integer. New data items are created with a use count of 1. On completion of the primitive, only the output roots that the boolean and integer were assigned to, will refer to those data items. Therefore a use count of 1 is correct.

binlist_find( )

To keep the search as fast as possible, pointers, register variables and bit-shifting by 1 instead of integer division by two have all been used here. The variable table is used as a pointer into the list. The variable lo is set to 0 and hi is set to the length of the list minus 1. Thus if the length of the list is 0, hi will be -1 and therefore less than lo. In that case the while loop is not executed, FALSE is returned for found and 0 is returned as the insert position of the string. Remember that, internally, lists use a 0 based index but when called from Prograph a 1 based index is used.

Next the hit point is computed. This is (lo + hi) divided by 2. The general principle of binary search is the process of divide and conquer. Because the list is in sorted order we first divide it into two parts, determine which of the two parts the string being sought belongs to, concentrate on that part then repeat the process until the string is matched or lo becomes greater than hi.

Hence if the value of compString is less than the value of the string in table[hit], the value of lo stays the same and the value of hi is set to hit minus 1. Otherwise if the value of compString is greater than the value of the string in table[hit], the value of hi stays the same and the value of lo is set to hit plus 1.

The process will then continue to loop while lo is less than or equal to hi, at which point lo will be left pointing to the place in the table where compString should be inserted.

The only other possibility is that the strings are equal, in which case found is set to TRUE and index is set to hit.

Note that in this implementation it is necessary that hi can assume negative values. This restricts us to lists of 231 items in length.


This is a simple string comparison routine. Although it works on two handles it is not necessary to lock them because nothing in this routine causes memory to be moved in the heap.

The Example Prograph

In order to run some simple timing tests on the two new primitives we shall construct a Prograph which generates a list of strings from the permutations of the characters of a starting string, creates a sorted list and then searches the sorted list for an entry. See the file binlist_test.pgs.

If, for each subsequent execution of the test program, you use as your starting strings “a”, “ab”, “abc” etc then the permutation algorithm will generate N factorial strings ( where N is the number of characters in the string ), each of which will be unique, ( because all of the characters of the starting string are unique ). If the starting string is used as the string for which to search then we are guaranteed to find it in the list and it is guaranteed to take the longest possible search time because it will always sort to the very first place in the list. You can verify that by a consideration of the code for binlist_find above.

One point to note is that 8! = 40320. 40320 strings will require more than 1 megabyte of RAM and even on an FX will tie your computer up for a while.

The Local Method permute first calls the primitive ask, which puts up a dialog box and asks you to input a string. If you do so then that string will be permuted into the required list of strings, which is done in the time permute Local Method. Both the starting string and the list will be saved in the persistents string and perms. The time taken to accomplish the permutations is then sent to the Method do-show which displays the length of the created list and the number of ticks ( i.e. 60ths of a second ) that it took.

You have the option here of not inputting a string and just pressing the return key or clicking the OK button of the ask dialog. In that case NULL is returned and Case 2 of permute is executed.

Case 2 of permute just outputs the list of permutations which were computed during a previous execution and saves having to recreate the list every time.

For the sake of comparison the permutation process is timed as well as the sort and search. The timing mechanism is very simple and, as we shall see later, a little too rough-grained for all our purposes. It consists of getting the value of the Mac global Ticks before and after the call you want to time. Then one is subtracted from the other to get the elapsed ticks.

The actual permutation process occurs in the Method do-perm.

Case 1 of do-perm first checks to see if the input is the empty string. If the string is not empty it is split into two parts; the first character and the rest of the string. The rest of the string is passed to a recursive call to do-perm and the recursion continues until the length of the input string is 0. At that point Case 2 of do-perm is executed and the recursion terminates, outputing a list which contains the empty string, to the root of the most recent do-perm call.

The string(s) in this list are then fed one at a time into the right input of the Local Method make string list. The character which was removed by the prefix operator in do-perm is also fed to make string list on its left input.

1 is added to the length of the string on the right input and a list that length of the form ( 0 1 2 3 etc ) is made. The input character and the input string are then fed repeatedly to the Local Method make string together with an integer from the list of numbers. The strings generated by make string are accumulated on its list output.

If, for example, the inputs are “a”, “bc” and 0 then the output string will be “abc”. If input 3 is 1 instead of 0 then the output string will be “bac”, and so on.

At the end of all recursions and iterations a list of lists of strings will be present on the output of make string list in the Method do-perm. But what is needed is a list of strings. It is the function of the (join) operation to accomplish that. The empty list is fed into its left input and is looped back around as each list of strings in the list of lists is joined to the empty list to give the final output of do-perm. This eventually returns through the Local Method time perm to the Local Method permute where the results can be displayed by a call to do-show.

The do-show Method puts up a dialog with a line of text in it of the form:

permute ( or sort or find): N strings: M ticks.

The unsorted list is next fed into the Local Method sort.

Which feeds it to time sort.

It is here that one of the new primitives that we created is first used. The unsorted list is fed to the input of binlist_sort and the new sorted list comes from its output. This eventually feeds back up to sort and then into the Local Method find.

Here the original string is retrieved from the persistent where it was saved and fed to the Local Method time find.

The second primitive, binlist-find, is called and the result of that timing is fed back for display to find.

The Results

The tests were executed on a Mac Plus with 4 Megabytes of RAM running under Finder. Here is a tabulation of the results. All times are in ticks, i.e. 1/60ths of a second.

Memory management, i.e. handle allocation, is the major cost both in generating the string permutations and for the sort operation. Nevertheless, for the maintenance of short lists, 2 seconds to sort 720 strings should be quite acceptable for many applications.

The find operation is faster than can be properly timed using the Mac’s tick count. It appears here to be absolutely steady. Other tests that have been carried out indicate an average search time of about 0.0002 seconds for a 10 element list, 0.0003 seconds for a 100 element list and 0.0005 seconds for a 1000 element list. In fact if N is the number of entries in the list the total number of times through the search loop is only about log2N, and the only memory management overhead involved is generating the handles for the primitive’s boolean and integer return values.

It would be simple enough to extend the binary search mechanism to accommodate duplicate entries. All that is necessary when a hit matches the input string, is to search forward and back through contiguous table entries to find all identical strings. Then the indices of the first and last matching entries can be returned. It would also be quite straightforward to develop versions of binlist-sort to sort lists of integers or reals, or of instances based upon the contents of any given instance attribute. As usual, these extensions are left as an exercise for the reader.

LISTING of binlist_prims_I.r



binlist-sort, 1 (32)
Inputs: InList\0D++
Outputs: OutList
Inputs: list\0D++
Outputs: list
Accept a list of strings, output a new list with the strings ++
sorted in ascending alphabetic order. Duplicate strings are ++
not included in OutList.

binlist-find, 2 (32)
Inputs: TheList; TheSearchString\0D++
Outputs: Found; TheIndex
Inputs: list; string\0D++
Outputs: boolean; integer 
Search TheList for a string equal to TheSearchString. ++
If found Found is TRUE and TheIndex is where it was found. ++
Otherwise Found is FALSE and TheIndex  is where ++
TheSearchString should go in TheList, if you wish to add it.
LISTING of binlist_prims.c

#include “MacTypes.h”
#include “X_includes.h”

/* size of a string and a list differ in interp. & compiled code */

 typedef Nat2 Index;
 typedef Nat4 Index;
#define LT-1
#define EQ0
#define GT1

/* prototypes for functions in this file */

Nat2 U_binlist_2D_sort( C_list *, C_list ** );
Nat2 U_binlist_2D_find( C_list *, C_string *, C_boolean **, C_integer 
** );
void binlist_find( C_list *, C_string *, Bool *, Index * );
Int2 compare_strings( C_string *, C_string * );


/* common table for supplied functions */

ProcPtr * ct;

/* main loads primitives into prograph interpreter’s primitive table 

void main( table )
ProcPtr * table; /* common table */
 move.l a4, -(sp)  /* move A4 on to the stack */
 move.l a0, a4   /* move address of code res. to A4 */
 ct = table;/* set up common table for shared functions */
 AddPrimitive( 1, 0x0101, PF_USER, 0, &U_binlist_2D_sort );
 AddPrimitive( 2, 0x0202, PF_USER, 0, &U_binlist_2D_find );

 asm { move.l (sp)+, a4 }/* move saved value back into A4 */


/* input list of strings, output sorted list of unique strings */

Nat2 U_binlist_2D_sort( inList, outList )
C_list *inList;  /* unsorted list */
C_list **outList;/* new sorted list */
Nat2 inarity;    /* number of inputs */
Nat2 outarity;   /* number of outputs */
register Index size; /* size of unsorted list */
register Index index;/* index in unsorted list */
Index sortIndex; /* index in sorted list */
C_string *curString; /* current string of inList */
C_list *sortList;/* the sorted list */
Bool found; /* TRUE if string found */

 GETARITY( inarity, outarity )

 if ( inarity != 1 || outarity != 1 )

 if ( !IsType( (C_object *)inList, C_LIST )  )
 return PRIMERR_TYPE + 1;

 sortList = MakeC_list( 0 );
 for ( index = 0, size = (**inList).length; index < size; index++ )
 curString = (C_string *)((**inList).data[index]);
 if ( !IsType( (C_object *) curString, C_STRING ) )
 DecUse( sortList );
 return PRIMERR_TYPE + 1;

 binlist_find( sortList, curString, &found, &sortIndex );
 if ( found == FALSE )
 ListInsertSlot( sortList, sortIndex, TRUE, (C_object *)curString );
 *outList = sortList;
 return PCF_TRUE;

/* primitive to find string in alpha sorted list by matching string */
Nat2 U_binlist_2D_find( input1, input2, output1, output2 )
C_list *input1;  /* sorted list of strings */
C_string *input2;/* string for key comparison */
C_boolean **output1; /* boolean is TRUE if found */
C_integer **output2; /* index found or new position */
Nat2 inarity;    /* number of inputs */
Nat2 outarity;   /* number of outputs */
Bool found; /* TRUE if found */
Index index;/* index found or new position */

 GETARITY( inarity, outarity );
 if ( inarity != 2 || outarity != 2 )

 if ( !IsType( (C_object *) input1, C_LIST )  )
 return PRIMERR_TYPE + 1;

 if ( !IsType( (C_object *) input2, C_STRING )  )
 return PRIMERR_TYPE + 2;
 binlist_find( input1, input2, &found, &index );
 *output1 = MakeC_boolean( found );
 *output2 = MakeC_integer( (Nat4)index + 1 );
 return PCF_TRUE;

/* find string in alpha sorted table by matching string */

void binlist_find( list, compString, found, index )
C_list *list;    /* sorted list */
C_string *compString;/* string for key comparison */
Bool *found;/* TRUE if found */
Index *index;    /* index found or index to put at */
register Int4 hit; /* table hit point */
register Int4 lo;/* table low bound */
register Int4 hi;/* table high bound */
register C_string **table;/* pointer to start of table */
register Bool compare;    /* result of string comparison */

 table = (C_string **)((**list).data);
 hi = (Int4)((**list).length) - 1;
 lo = 0;
 while ( lo <= hi )
 hit = ( lo + hi) >> 1;
 compare = compare_strings( compString, table[hit] );
 if ( compare == LT )
 hi = hit - 1;
 else if ( compare == GT )
 lo = hit + 1;
 *index = hit;
 *found = TRUE;
 *index = lo;
 *found = FALSE;

/* compare_strings - compare two strings. return LT -1, EQ 0, GT 1  */
Int2 compare_strings( string1, string2 )
C_string *string1; /* first string */
C_string *string2; /* second string */
register Index ind1; /* length of 1st string */
register Index ind2; /* length of 2nd string */
register Nat1 *ptr1; /* pointer into 1st string */
register Nat1 *ptr2; /* pointer into 2nd string */

 for( ind1 = (**string1).length, 
  ind2 = (**string2).length,
  ptr1 = (**string1).text, 
  ptr2 = (**string2).text ;
  ind1 && ind2 ; 
  ind1--, ind2--, ptr1++, ptr2++ )
 if ( *ptr1 < *ptr2 )
 return LT;
 else if ( *ptr1 > *ptr2 )
 return GT;
 if ( ind1 )
 return GT;
 else if ( ind2 )
 return LT;
 return EQ;


Community Search:
MacTech Search:

Software Updates via MacUpdate

Airmail 3.3.2 - Powerful, minimal email...
Airmail is an mail client with fast performance and intuitive interaction. Support for iCloud, MS Exchange, Gmail, Google Apps, IMAP, POP3, Yahoo!, AOL,, Airmail was designed... Read more
Numi 3.15.1 - Menu-bar calculator suppor...
Numi is a calculator that magically combines calculations with text, and allows you to freely share your computations. Numi combines text editor and calculator Support plain English. For example, '5... Read more
TextSoap 8.4.1 - Automate tedious text d...
TextSoap can automatically remove unwanted characters, fix up messed up carriage returns, and do pretty much anything else that we can think of to text. Save time and effort. Be more productive. Stop... Read more
TextSoap 8.4.1 - Automate tedious text d...
TextSoap can automatically remove unwanted characters, fix up messed up carriage returns, and do pretty much anything else that we can think of to text. Save time and effort. Be more productive. Stop... Read more
Backblaze - Online backup servi...
Backblaze is an online backup service designed from the ground-up for the Mac. With unlimited storage available for $5 per month, as well as a free 15-day trial, peace of mind is within reach with... Read more
Numi 3.15 - Menu-bar calculator supports...
Numi is a calculator that magically combines calculations with text, and allows you to freely share your computations. Numi combines text editor and calculator Support plain English. For example, '5... Read more
EtreCheck 3.3.3 - For troubleshooting yo...
EtreCheck is an app that displays the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support Communities to... Read more
BusyContacts 1.1.8 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
TunnelBear 3.0.14 - Subscription-based p...
TunnelBear is a subscription-based virtual private network (VPN) service and companion app, enabling you to browse the internet privately and securely. Features Browse privately - Secure your data... Read more
Apple Final Cut Pro X 10.3.4 - Professio...
Apple Final Cut Pro X is a professional video editing solution.Completely redesigned from the ground up, Final Cut Pro adds extraordinary speed, quality, and flexibility to every part of the post-... Read more

Latest Forum Discussions

See All

The best new games we played this week
We were quite busy this week. A bunch of big mobile games launched over the past few days, alongside a few teeny surprises. There're lots of quality games to load your phone with. We've gone and picked out five of our favorites for the week. [... | Read more »
Magikarp Jump beginner's guide
Magikarp Jump is a mystifying little game. Part Tamagotchi, part idle clicker, there's not a whole lot of video game there, per se, but for some reason we can't help coming back to it again and again. Your goal is to train up a little Magikarp to... | Read more »
Goat Simulator PAYDAY (Games)
Goat Simulator PAYDAY 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: ** IMPORTANT - SUPPORTED DEVICES **iPhone 4S, iPad 2, iPod Touch 5 or better Goat Simulator: Payday is the most... | Read more »
GRID Autosport delayed until autumn
Sorry mobile racing fans -- GRID Autosport has been delayed a few months. The game is now expected to launch this fall on iOS. Feral Interactive announced that they wanted more time to work on the game's UI and overall performance before launching... | Read more »
Zombie Gunship Survival Beginner's...
The much anticipated Zombie Gunship Survival is here. In this latest entry in the Zombie Gunship franchise, you're tasked with supporting ground troops and protecting your base from the zombie horde. There's a lot of rich base building fun, and... | Read more »
Mordheim: Warband Skirmish (Games)
Mordheim: Warband Skirmish 1.2.2 Device: iOS Universal Category: Games Price: $3.99, Version: 1.2.2 (iTunes) Description: Explore the ruins of the City of Mordheim, clash with other scavenging warbands and collect Wyrdstone -... | Read more »
Mordheim: Warband Skirmish brings tablet...
Legendary Games has just launched Mordheim: Warband Skirmish, a new turn-based action game for iOS and Android. | Read more »
Magikarp Jump splashes onto Android worl...
If you're tired ofPokémon GObut still want something to satisfy your mobilePokémon fix,Magikarp Jumpmay just do the trick. It's out now on Android devices the world over. While it looks like a simple arcade jumper, there's quite a bit more to it... | Read more »
Purrfectly charming open-world RPG Cat Q...
Cat Quest, an expansive open-world RPG from former Koei-Tecmo developers, got a new gameplay trailer today. The video showcases the combat and exploration features of this feline-themed RPG. Cat puns abound as you travel across a large map in a... | Read more »
Jaipur: A Card Game of Duels (Games)
Jaipur: A Card Game of Duels 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: ** WARNING: iPad 2, iPad Mini 1 & iPhone 4S are NOT compatible. ** *** Special Launch Price for a limited... | Read more »

Price Scanner via

Memorial Day savings: 13-inch Touch Bar MacBo...
B&H Photo has the 2016 Apple 13″ Touch Bar MacBook Pros in stock today and on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 13″ 2.9GHz/512GB... Read more
Apple refurbished 13-inch MacBook Airs availa...
Apple has Certified Refurbished 2016 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 13″ 1.6GHz/8GB/128GB MacBook Air: $... Read more
Apple restocks refurbished 11-inch MacBook Ai...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models recently discontinued by Apple), available for up to $170 off original MSRP. An Apple one-year warranty is included with each... Read more
12-inch 1.2GHz Retina MacBooks on sale for up...
B&H has 12″ 1.2GHz Retina MacBooks on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 12″ 1.2GHz Space Gray Retina MacBook: $1449.99 $150 off... Read more
15-inch 2.7GHz Silver Touch Bar MacBook Pro o...
MacMall has the 15-inch 2.7GHz Silver Touch Bar MacBook Pro (MLW82LL/A) on sale for $2569 as part of their Memorial Day sale. Shipping is free. Their price is $230 off MSRP. Read more
Free Tread Wisely Mobile App Endorsed By Fath...
Just in time for the summer driving season, Cooper Tire & Rubber Company has announced the launch of a new Tread Wisely mobile app. Designed to promote tire and vehicle safety among teens and... Read more
Commercial Notebooks And Detachable Tablets W...
Worldwide shipments of personal computing devices (PCDs), comprised of traditional PCs (a combination of desktop, notebook, and workstations) and tablets (slates and detachables), are forecast to... Read more
Best value this Memorial Day weekend: Touch B...
Apple has Certified Refurbished 2016 15″ and 13″ MacBook Pros available for $230 to $420 off original MSRP. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2.6GHz... Read more
13-inch MacBook Airs on sale for up to $130 o... has 13″ MacBook Airs on sale for up to $130 off MSRP including free shipping: - 13″ 1.6GHz/128GB MacBook Air (sku MMGF2LL/A): $869.99 $130 off MSRP - 13″ 1.6GHz/256GB MacBook Air (sku... Read more
2.8GHz Mac mini available for $973 with free...
Adorama has the 2.8GHz Mac mini available for $973, $16 off MSRP, including a free copy of Apple’s 3-Year AppleCare Protection Plan. Shipping is free, and Adorama charges sales tax in NY & NJ... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
Best Buy *Apple* Computing Master - Best Bu...
**509643BR** **Job Title:** Best Buy Apple Computing Master **Location Number:** 001482- Apple Valley-Store **Job Description:** **What does a Best Buy Apple Read more
*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.