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.

compare_strings()

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_prims.p.rsrc

TYPE STR#

binlist-sort, 1 (32)
3
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)
3
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 */

#if _INTERPRETER
 typedef Nat2 Index;
#else
 typedef Nat4 Index;
#endif
 
#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 * );

#if _INTERPRETER

/* common table for supplied functions */

ProcPtr * ct;

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

void main( table )
ProcPtr * table; /* common table */
{
 asm 
 { 
 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 */
}

#endif

/* 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 */

#if _INTERPRETER
 GETARITY( inarity, outarity )

 if ( inarity != 1 || outarity != 1 )
 return PRIMERR_ARITY;

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

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

 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 */

#if _INTERPRETER
 GETARITY( inarity, outarity );
 
 if ( inarity != 2 || outarity != 2 )
 return PRIMERR_ARITY;

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

 if ( !IsType( (C_object *) input2, C_STRING )  )
 return PRIMERR_TYPE + 2;
#endif
 
 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;
 else
 {
 *index = hit;
 *found = TRUE;
 return;
 }
 }
 
 *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;
 else
 return EQ;
}

 
AAPL
$99.18
Apple Inc.
-1.57
MSFT
$45.90
Microsoft Corpora
-0.46
GOOG
$568.27
Google Inc.
-9.09

MacTech Search:
Community Search:

Software Updates via MacUpdate

Cocktail Family License (5 Macs) 7.6.1 -...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more
Cocktail 8.0 Beta 2 - General maintenanc...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more
QuickBooks 2015 16.0.0.1352 R1 - Financi...
QuickBooks 2015 helps you manage your business easily and efficiently. Organize your finances all in one place, track money going in and out of your business, and spot areas where you can save.... Read more
Mac DVDRipper Pro 5.0.1 - 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
Apple OS X bash Update 1.0 - Fix for sec...
The OS X bash Update fixes a security flaw in the bash UNIX shell on OS X 10.9.5 (also on OS X 10.8 and 10.7 [see Related Links below]). OS X 10.9.5 or later Downloads for OS X 10.8 and OS X 10.7 in... Read more
SyncTwoFolders 2.0.5 - Syncs two user-sp...
SyncTwoFolders simply synchronizes two folders. It supports synchronization across mounted network drives and it is a possibility to run a simulation showing in a log what will be done. Please visit... Read more
FinderPop 2.5.7 - Classic Mac utility, n...
FinderPop is a Universal preference pane that extends OS X's contextual menus using a FinderPop Items folder much as the Apple Menu Items folder used to do for the Apple menu. It has other features... Read more
VueScan 9.4.45 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
LibreOffice 4.3.2.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
calibre 2.4 - Complete e-library managem...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more

Latest Forum Discussions

See All

iKeywi - Customizable 5-Row Keyboard (U...
iKeywi - Customizable 5-Row Keyboard 1.0 Device: iOS Universal Category: Utilities Price: $.99, Version: 1.0 (iTunes) Description: Want to add an extra row to your iPhone/iPad? One of the most popular keyboard extension in iOS... | Read more »
Manage Your Cloud – Wunderlist Now Suppo...
Manage Your Cloud – Wunderlist Now Supports Dropbox Posted by Jessica Fisher on October 1st, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Nexticy Review
Nexticy Review By Jennifer Allen on October 1st, 2014 Our Rating: :: IDEAL FORM CREATIONiPad Only App - Designed for the iPad Nexticy allows you to make your own forms for research purposes or to organize your business better. It’s... | Read more »
Tiny Troopers: Alliance Marches onto the...
Tiny Troopers: Alliance Marches onto the App Store Tomorrow Posted by Jessica Fisher on October 1st, 2014 [ permalink ] Tiny Troopers: Alliance, by Kukouri, is a | Read more »
HeroCraft Introduces Unlimited Sequel to...
HeroCraft Introduces Unlimited Sequel to WW2: Sandbox. Strategy & Tactics Posted by Jessica Fisher on October 1st, 2014 [ permalink ] | Read more »
RGB Express Review
RGB Express Review By Jennifer Allen on October 1st, 2014 Our Rating: :: DELIGHTFUL PUZZLINGUniversal App - Designed for iPhone and iPad Guide trucks along their delivery routes in RGB Express, a testing but charming puzzle game... | Read more »
The Sagas of Fire*Wolf (Games)
The Sagas of Fire*Wolf 1.0 Device: iOS Universal Category: Games Price: $9.99, Version: 1.0 (iTunes) Description: | Read more »
BuggyFun Review
BuggyFun Review By Amy Solomon on October 1st, 2014 Our Rating: iPad Only App - Designed for the iPad BuggyFun allows children to create their own tracks for bugs to interact with for a unique open-ended experience.   | Read more »
Fold the Adventure Review
Fold the Adventure Review By Jennifer Allen on October 1st, 2014 Our Rating: :: AWKWARD FOLDSUniversal App - Designed for iPhone and iPad Fold pieces of paper to create platforms for a princely rabbit in this puzzle game; something... | Read more »
WW2: Sandbox. Strategy & Tactics (G...
WW2: Sandbox. Strategy & Tactics 1.0.0 Device: iOS Universal Category: Games Price: $9.99, Version: 1.0.0 (iTunes) Description: ***NOTE: Compatible with iPhone 4s and up, iPad 2 and up - may not work properly on earlier devices... | Read more »

Price Scanner via MacPrices.net

Amazon offers 13-inch MacBook Air for $899, $...
Amazon.com has the 13″ 1.4GHz 128GB MacBook Air on sale for $100 off MSRP including free shipping: - 13″ 1.4GHz 128GB MacBook Air: $899.99 Read more
Apple resting On Its iPhone Laurels? – The ‘B...
Apple calls its new iPhone 6 and 6 Plus “The Biggest Advancements in iPhone History,” but does reality live up to the hype? “Seldom have so many waited so breathlessly for so little,” tweeted veteran... Read more
Roundup of Apple Mac and iPad Education disco...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
Apple Boycotts German Magazine Computer Bild...
Apple has revoked its PR accreditation of Germany’s Computer Bild, Europe’s best-selling PC magazine, in reaction to Bild’s posting of a “#Bentgate” YouTube video. Axel Telzerow, editor in chief of... Read more
iPhone 6 & iPhone 6 Plus Available in Chi...
Apple has announced that iPhone 6 and iPhone 6 Plus will be available in China beginning Friday, October 17 from the Apple Online Store (http://www.apple.com), Apple’s retail stores, and an expansive... Read more
MacBook Airs on sale for $100 off MSRP, start...
Best Buy has the new 2014 MacBook Airs on sale for $100 off MSRP on their online store. Choose free home shipping or free local store pickup (if available). Prices valid for online orders only, in-... Read more
Apple Releases OS X Mavericks bash Update 1.0...
Apple has released a patch update for OS X Mavericks users to address the recently-detected “Shellshock” security bug in BSD UNIX’s bash shell. Apple says only a few Mac users who had manually... Read more
Pivotal Payments Ready for Apple Pay – FlexPo...
Pivotal Payments, a provider of merchant services and global payment processing solutions, has announced its proprietary FlexPoint platform will support credit and debit transactions through Apple’s... Read more
iStabilizer Announces Tabarm — First Friction...
iStabilizer, a specialist in universal lightweight compact tripods, steady cams, dollies, mounts, and remotes for smartphones, tablets, and cameras, announced today the iStabilizer tabArm, the first... Read more
IStabilizer Flex Smartphone Tripod Wins Usa T...
iStabilizer, a specialist in universal lightweight compact tripods, steady cams, and other products for smartphones, tablets, and cameras, has announced today that its iStabilizer Flex smartphone... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*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
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.