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

pwSafe 4.0.1 - Secure password managemen...
pwSafe provides simple and secure password management across devices and computers. pwSafe uses iCloud to keep your password databases backed-up and synced between Macs and iOS devices. It is... Read more
WALTR 1.5.4 - Drag-and-drop any media fi...
WALTR is designed to make it easy to upload and convert any music or video file to an iPad or iPhone format for native playback. It supports a huge variety of media file types, including MP3, MP4,... Read more
Audio Hijack 3.1 - Record and enhance au...
Audio Hijack (was Audio Hijack Pro) drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio... Read more
PopChar 7.1 - Floating window shows avai...
We're also selling a 5-license family pack for only $25.99! PopChar helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to... Read more
BBEdit 11.1.1 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
Picasa 3.9.139 - Organize, edit, and sha...
Picasa and Picasa Web Albums allows you to organize, edit, and upload your photos to the Web from your computer in quick, simple steps. Arrange your photos into folders and albums and erase their... Read more
Mac DVDRipper Pro 5.0.5 - Copy, backup,...
Mac DVDRipper Pro is the DVD backup solution that lets you protect your DVDs from scratches, save your batteries by reading your movies from your hard disk, manage your collection with just a few... Read more
NetShade 6.2 - Browse privately using an...
This promotion is for NetShade and 1 year of Proxy and VPN services NetShade is an anonymous proxy and VPN app+service for Mac. Unblock your Internet through NetShade's high-speed proxy and VPN... Read more
CrossOver 14.1.3 - Run Windows apps on y...
CrossOver can get your Windows productivity applications and PC games up and running on your Mac quickly and easily. CrossOver runs the Windows software that you need on Mac at home, in the office,... Read more
Little Snitch 3.5.3 - Alerts you about o...
Little Snitch gives you control over your private outgoing data. Track background activity As soon as your computer connects to the Internet, applications often have permission to send any... Read more

Block Fortress has a Big New Update for...
Block Fortress is a survival-style game that's as fun as it is blocky. It's also just gotten a rather sizeable update that adds a lot more cool stuff. [Read more] | Read more »
Simple and Surreal Star Base Sim rymdkap...
I really like rymdkapsel. Not just because I'm a sucker for games that are cleverly simple or highly stylisitc, but because it's fun and challenging. Actually it's extremely challenging, which is why I was excited to learn that it's getting a couple... | Read more »
Check out the anticipated Angel Stone in...
Fincon has finally revealed Angel Stone in action in the first ever official gameplay trailer for the anticipated hack and slasher. Angel Stone is set in a post-apocalyptic world in which humanity is in danger of being wiped out by the demonic... | Read more »
Moleskine Timepage is an All-New Calenda...
Moleskine Timepage is a bit of a departure for the notebook manufacturer (since it has little to do with notebooks), but it certainly carries their simple and elegant style quite well. [Read more] | Read more »
Jog on Over and Check Out the New Runtas...
Runtastic has put out a fair number of apps to help you sleep, track excercise, and train various parts of your body. Now it's time for your legs to have their own time in the spotlight with Runtastic Leg Trainer. [Read more] | Read more »
It's Lights Out in the Upcoming Pla...
Ember’s Journey is a stark puzzle platformer with a twist: the entire game is played in darkness. The only light you can see by is the one emanating from your own character. [Read more] | Read more »
MooVee - Your Movies Guru (Entertainmen...
MooVee - Your Movies Guru 1.0 Device: iOS iPhone Category: Entertainment Price: $1.99, Version: 1.0 (iTunes) Description: MooVee helps you effortlessly manage your movies, on your iPhone. | Read more »
Geometry Wars 3: Dimensions (Games)
Geometry Wars 3: Dimensions 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: Enjoy the next chapter in the award-winning Geometry Wars franchise and enjoy stunning, console-quality... | Read more »
CHAOS RINGS Ⅲ 1.0.0 Device: iOS Universal Category: Games Price: $19.99, Version: 1.0.0 (iTunes) Description: The newest addition to the popular smartphone RPG series is finally here! ・CHAOS RINGS Overview | Read more »
The Popular Insight Series of Travel Gui...
Getting around in a country when you can't understand the primary language can be tough. Fortunately there are several options available to help wold travellers with the important stuff like giving directions to a cab driver or asking where the... | Read more »

Price Scanner via

Top Markets Saturation To Slow Global Smartph...
According to a new mobile phone forecast from the International Data Corporation (IDC) Worldwide Quarterly Mobile Phone Tracker, smartphone shipments are expected to grow 11.3% in 2015 — down from 27... Read more
Apple refurbished 2014 13-inch Retina MacBook...
The Apple Store has Apple Certified Refurbished 2014 13″ Retina MacBook Pros available for up to $400 off original MSRP, starting at $979. An Apple one-year warranty is included with each model, and... Read more
What Would the ideal Apple Productivity Platf...
For the past four years I’ve kept a foot in both the Mac and iPad camps respectively. my daily computing hours divided about 50/50 between the two devices with remarkable consistency. However, there’... Read more
PageMeUp 1.2.1 Ten Dollar Page Layout Applica...
Paris, France-based Softobe, an OS X software development company, has announced that their PageMeUp v. 1.2.1, is available on the Mac App Store for $9.99. The license can be installed on up to 5... Read more
Eight New Products For USB Type-C Application...
Fresco Logic, specialists in advanced connectivity technologies and ICs, has introduced two new product families targeting the Type-C connector recently introduced across a number of consumer... Read more
Scripps National Spelling Bee Launches Buzzwo...
Scripps National Spelling Bee fans can monitor the action at the 2015 Spelling Bee with the new Buzzworthy app for iOS, Android and Windows mobile devices. The free Buzzworthy app provides friendly... Read more
13-inch 2.5GHz MacBook Pro on sale for $120 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $979 including free shipping plus NY sales tax only. Their price is $120 off MSRP, and it’s the lowest price for this model (except for Apple’... Read more
27-inch 3.3GHz 5K iMac on sale for $1899, $10...
B&H Photo has the new 27″ 3.3GHz 5K iMac on sale for $1899.99 including free shipping plus NY tax only. Their price is $100 off MSRP. Read more
Save up to $50 on iPad Air 2, NY tax only, fr...
B&H Photo has iPad Air 2s on sale for up to $50 off MSRP including free shipping plus NY sales tax only: - 16GB iPad Air 2 WiFi: $469 $30 off - 64GB iPad Air 2 WiFi: $549.99 $50 off - 128GB iPad... Read more
Updated Mac Price Trackers
We’ve updated our Mac Price Trackers with the latest information on prices, bundles, and availability on systems from Apple’s authorized internet/catalog resellers: - 15″ MacBook Pros - 13″ MacBook... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Program Manager, *Apple* Community Support...
**Job Summary** Apple Support Communities ( discussions. apple .com) helps customers get the most from their Apple products and services by providing access to Read more
Senior Data Scientist, *Apple* Retail - Onl...
**Job Summary** Apple Retail - Online sells Apple products to customers around the world. In addition to selling Apple products with unique services such as iPad Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Watch SW Application Project Manager...
**Job Summary** The Apple Watch software team is looking for an Application Engineering Project Manager to work on new projects for Apple . The successful candidate Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.