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
$111.78
Apple Inc.
-0.87
MSFT
$47.66
Microsoft Corpora
+0.14
GOOG
$516.35
Google Inc.
+5.25

MacTech Search:
Community Search:

Software Updates via MacUpdate

CleanApp 5.0.0 Beta 5 - Application dein...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more
Monolingual 1.6.2 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. It requires a 64-bit capable Intel-based Mac and at least... Read more
NetShade 6.1 - Browse privately using an...
NetShade is an Internet security tool that conceals your IP address on the web. NetShade routes your Web connection through either a public anonymous proxy server, or one of NetShade's own dedicated... Read more
calibre 2.13 - Complete e-library manage...
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 librarian... Read more
Mellel 3.3.7 - Powerful word processor w...
Mellel is the leading word processor for OS X and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
ScreenFlow 5.0.1 - Create screen recordi...
Save 10% with the exclusive MacUpdate coupon code: AFMacUpdate10 Buy now! ScreenFlow is powerful, easy-to-use screencasting software for the Mac. With ScreenFlow you can record the contents of your... Read more
Simon 4.0 - Monitor changes and crashes...
Simon monitors websites and alerts you of crashes and changes. Select pages to monitor, choose your alert options, and customize your settings. Simon does the rest. Keep a watchful eye on your... Read more
BBEdit 11.0.2 - 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
ExpanDrive 4.2.1 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
Adobe After Effects CC 2014 13.2 - Creat...
After Effects CC 2014 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous After Effects customer). After Effects CS6 is still available... Read more

Latest Forum Discussions

See All

Make your own Tribez Figures (and More)...
Make your own Tribez Figures (and More) with Toyze Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
So Many Holiday iOS Sales Oh My Goodness...
The holiday season is in full-swing, which means a whole lot of iOS apps and games are going on sale. A bunch already have, in fact. Naturally this means we’re putting together a hand-picked list of the best discounts and sales we can find in order... | Read more »
It’s Bird vs. Bird in the New PvP Mode f...
It’s Bird vs. Bird in the New PvP Mode for Angry Birds Epic Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Telltale Games and Mojang Announce Minec...
Telltale Games and Mojang Announce Minecraft: Story Mode – A Telltale Games Series Posted by Jessica Fisher on December 19th, 2014 [ permalink ] | Read more »
WarChest and Splash Damage Annouce Their...
WarChest and Splash Damage Annouce Their New Game: Tempo Posted by Jessica Fisher on December 19th, 2014 [ permalink ] WarChest Ltd and Splash Damage Ltd are teaming up again to work | Read more »
BulkyPix Celebrates its 6th Anniversary...
BulkyPix Celebrates its 6th Anniversary with a Bunch of Free Games Posted by Jessica Fisher on December 19th, 2014 [ permalink ] BulkyPix has | Read more »
Indulge in Japanese cuisine in Cooking F...
Indulge in Japanese cuisine in Cooking Fever’s new sushi-themed update Posted by Simon Reed on December 19th, 2014 [ permalink ] Lithuanian developer Nordcurrent has yet again updated its restaurant simulat | Read more »
Badland Daydream Level Pack Arrives to C...
Badland Daydream Level Pack Arrives to Celebrate 20 Million Downloads Posted by Ellis Spice on December 19th, 2014 [ permalink ] | Read more »
Far Cry 4, Assassin’s Creed Unity, Desti...
Far Cry 4, Assassin’s Creed Unity, Destiny, and Beyond – AppSpy Takes a Look at AAA Companion Apps Posted by Rob Rich on December 19th, 2014 [ permalink ] These day | Read more »
A Bunch of Halfbrick Games Are Going Fre...
A Bunch of Halfbrick Games Are Going Free for the Holidays Posted by Ellis Spice on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

The Apple Store offering free next-day shippi...
The Apple Store is now offering free next-day shipping on all in stock items if ordered before 12/23/14 at 10:00am PT. Local store pickup is also available within an hour of ordering for any in stock... Read more
It’s 1992 Again At Sony Pictures, Except For...
Techcrunch’s John Biggs interviewed a Sony Pictures Entertainment (SPE) employee, who quite understandably wished to remain anonymous, regarding post-hack conditions in SPE’s L.A office, explaining “... Read more
Holiday sales this weekend: MacBook Pros for...
 B&H Photo has new MacBook Pros on sale for up to $300 off MSRP as part of their Holiday pricing. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1699... Read more
Holiday sales this weekend: MacBook Airs for...
B&H Photo has 2014 MacBook Airs on sale for up to $120 off MSRP, for a limited time, for the Thanksgiving/Christmas Holiday shopping season. Shipping is free, and B&H charges NY sales tax... Read more
Holiday sales this weekend: iMacs for up to $...
B&H Photo has 21″ and 27″ iMacs on sale for up to $200 off MSRP including free shipping plus NY sales tax only. B&H will also include a free copy of Parallels Desktop software: - 21″ 1.4GHz... Read more
Holiday sales this weekend: Mac minis availab...
B&H Photo has new 2014 Mac minis on sale for up to $80 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 1.4GHz Mac mini: $459 $40 off MSRP - 2.6GHz Mac mini: $629 $70 off MSRP... Read more
Holiday sales this weekend: Mac Pros for up t...
B&H Photo has Mac Pros on sale for up to $500 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2599, $400 off MSRP - 3.5GHz 6-core Mac Pro: $3499, $... Read more
Save up to $400 on MacBooks with Apple Certif...
The Apple Store has Apple Certified Refurbished 2014 MacBook Pros and MacBook Airs available for up to $400 off the cost of new models. An Apple one-year warranty is included with each model, and... Read more
Save up to $300 on Macs, $30 on iPads with Ap...
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
iOS and Android OS Targeted by Man-in-the-Mid...
Cloud services security provider Akamai Technologies, Inc. has released, through the company’s Prolexic Security Engineering & Research Team (PLXsert), a new cybersecurity threat advisory. The... Read more

Jobs Board

*Apple* Store Leader Program (US) - Apple, I...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on experience, Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*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...
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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.