TweetFollow Us on Twitter

Ultra-Groovy LString Class

Volume Number: 15 (1999)
Issue Number: 1
Column Tag: PowerPlant Workshop

The Ultra-Groovy LString Class

by John C. Daub, Austin, Texas, USA

An introduction to the PowerPlant-way of working with strings

Hello, World!

Most software developers have been working with strings since they wrote their first program - that's what "Hello World!" is all about. To facilitate working with strings many libraries of utility functions have been written. Some people have their home-brewed string libraries, while others utilize third party libraries or use the libraries provided in Standard C and C++. These solutions are well and good, but they do not always translate very effectively into the world of Mac OS. Many of these offerings, like the Standard C and C++ libraries, are written to work with C-style strings - a sequence of characters terminated by a null character. But due to the Mac OS Toolbox's Pascal heritage, strings on the Mac are Pascal-style strings - a sequence of characters preceeded by a length byte which stores the length of the string. Consequently, a string library designed to work with Pascal-style strings is more valuable to the Mac OS software developer. Enter PowerPlant's LString class.

LString is a C++ abstract base class for working with Pascal-style strings. It provides means for converting strings to numbers and numbers to strings; obtain strings from numerous sources (characters, other strings, resources); copy, append, and compare strings; find, insert, remove, and replace substrings; and does so in a manner that respects the boundaries of Pascal-style strings, takes advantage of the C++ language, makes your code easier to read and understand, and most of all is easy and logical to use. The inherited classes LStr255, TString, and LStringRef provide the concrete means for working with strings in a manner very familiar to Mac OS software developers, so the learning curve for LString is not a steep one. And although LString is a PowerPlant class it can be used independent of the rest of PowerPlant; feel free to use it in all of your Mac OS software development efforts.

If LString sounds good to you, then please read on. In this article I hope to provide you with a good overview of what LString provides, and illustrate how it will make your software development efforts easier. Along the way I'll also show you a few of the really cool and groovy power-user features of LString. But don't worry! Those power-user features are so easy to use that you'll be an LString-Warrior before you know it!

First, Some Background

Before I go any further, I want to make sure that we're on the same grounds of understanding just what a string is, and what the differences are between the various types of strings. If you already understand what the different types of strings are, feel free to skip ahead to the next section.

Simply put, a string is a sequence of characters. This sentence is an example of a string.

A C-style string is an array of characters (char*). The internal representation of the string has a null character ('\0') at the end, to signal the end of the string, so the amount of storage required for the string is one more than the number of characters in the string. Due to the use of the null terminator, there is theoretically no limit to the length of a C-style string, but you must scan the entire string to determine the length of the string (the C standard library function strlen() can be used to determine the length of the string). So the string "Hello, World!" contains thirteen characters, but at least fourteen bytes must be allocated to hold that string.

A Pascal-style string is also an array of characters (unsigned char*). Like a C-style string, a Pascal-style string also requires one more than the number of characters in the string for storage. However unlike a C-style string (and this is the defining and differentiating characteristic) there is no null terminator at the end of the string; instead, the first byte of the string is used to store a count of the number of characters in the string. You do not need to scan the entire length of the string to determine its length - you can merely examine the first byte (length byte) of the string. Also unlike a C-style string, a Pascal-style string does have a limit; this limit is determined by the number of bytes allocated in the unsigned char* array (e.g. unsigned char[256] would allow a string up to 255 characters). As a Pascal-style string "Hello, World!" still contains thirteen characters and also needs at least fourteen bytes for storage, but the internal representation of the string is different. Finally, when specifying a Pascal-style string constant, they are typically prefaced by a \p to tell the compiler to treat this string as a Pascal-style string (e.g. "\pHello, World!" would be the actual way to represent our example string as a Pascal-style string). Figure 1 illustrates how the internal storage of the "Hello, World!" string differs as a C-style string and a Pascal-style string.


Figure 1. String internal representations.

In Figure 1, each column represents a byte in the array (note we start at zero, like any array). The top row is the byte count, the middle row is the C-style string, and the bottom row is the Pascal-style string. Notice that both string styles require the same amount of storage (fourteen bytes). The C-style string places a null terminator in the last byte (although it looks like two separate characters \0 is just a special single character). When the string is read in, characters are read one by one until the null terminator is reached. If we wanted to find the length of the C-style string we would scan the string, starting from the beginning, counting each character as we go along until we reach the null terminator (this is what strlen does). The Pascal-style string places the length of the string in the first byte of the array (in this case the number thirteen, not "13" as a string or character). When the string is read, the first byte is checked for the length, and then exactly that many characters are read. If we wanted to find the length of the string, we check the first byte (string[0]).

Both styles of strings have their strengths and weaknesses. C-style strings can be of an arbitrary length whereas Pascal-style strings tend to have a more fixed size. C-style strings also have more overhead involved in determining the length of the string (a function call and walking the entire string), but you can find the length of a Pascal-style string with a simple check of the length byte.

On the Mac OS, most strings are Pascal-style strings (since the OS and toolbox were originally written in Pascal). To more easily represent and identify strings, and provide a simple means for defining strings of different lengths, MacTypes.h (formerly Types.h) typedefs some arrays of unsigned char's. The most popular version is a Str255, which is an array of 256 unsigned char's. There are others (Str63, Str32, Str15, StringPtr, ConstStr255Param - see MacTypes.h for a complete listing), and although Str255's are the most commonly used, the discussions in this article apply to any of these type(def)s. The Mac OS can also have raw text runs (e.g. 'TEXT' resources, TERec, etc.), and you can use C-style strings as well. The only time you are "forced" to use a Pascal-style string is when you interact with the Toolbox. If you wish to use C-style strings otherwise, you are very welcome to do so, especially if you are more familiar with the C Standard Library. Furthermore, the Toolbox provides some Pascal to-from C string conversion routines (c2pstr and p2cstr are in the Universal Header TextUtils.h ), and even provides C-glue routines that are glue routines to Toolbox functions that take C-style strings as arguments instead (look for the symbol CGLUESUPPORTED throughout the Universal Headers).

But as most Mac OS software development sooner or later requires you to interact with the Toolbox, most people find it easier to use Pascal-style strings all the time (also avoids the constant overhead of back and forth conversions). Due to this preference is why PowerPlant offers a great solution in LString. Let's now take a look at what exactly LString has to offer.

What's in There?

The LString class (and all of the classes and material I'll discuss here) can be found within the PowerPlant folder of the Metrowerks CodeWarrior Professional product. Specifically within the files LString.cp and LString.h. If you own CodeWarrior Professional, go ahead and open up those two files and look over them. I don't expect you to necessarily understand what's in there right now, but give them a look over so you can know what I'm talking about. You might also find it handy to refer to the sources as I refer to various parts of it so you can see how all of this fits together. For reference, I am writing this article using CodeWarrior Pro 4 and a version of LString that should be part of PowerPlant v1.9.3 (which will be released as public netborne update to Pro 4, and should be available by the time you read this article).

LString

LString is an abstract base class for working with Pascal-style strings. It defines almost all of the functionality that one would need for working with Pascal-style strings. LString also works to take advantage of the C++ concept of overloading. First many of the methods are overloaded to work with many data types (all built-in data types, both signed and unsigned, including floating point types; C-style strings; Pascal-style strings; FourCharCode; a Handle to text; other LString objects). Second, many of the methods are also offered as operator overloads, were logical, to make it easier for you to utilize these methods in your code. For example, operator += is the same as the Append() method. LString also defines some public methods as static so that non-LString objects can take advantage of some commonly needed string manipulations like copying and appending.

LString is designed to be clean, efficient, and work within the boundaries of the Mac OS and the Pascal-style strings utilized therein. As noted earlier, MacTypes.h defines different types of Pascal-style strings, like a Str255 or a Str63. Due to this ability to vary in storage length, any time length of string and/or storage is relevant to the functionality, LString always requires this information be provided, but typically also provides a default towards a Str255 as this is the most commonly used type. Furthermore, since Mac OS Pascal-style strings have 255 characters as the upper limit for strings (hence the Str255), LString always enforces this upper boundary where appropriate. If utilized correctly, LString should alleviate fears and eliminate the problem of array boundary overflows.

Although PowerPlant is a Mac OS-only framework, it does try to do what it can to aid those that might be using PowerPlant in a non-Mac context such as porting your Mac OS-based application to Windows. Some LString methods are only available under Mac OS (due to their nature), and techniques are provided for development environments that might not fully understand Mac OS conventions. For example, the traditional way to obtain the length of the string is to directly check the length byte. But since not all development environments support this method of access (of course CodeWarrior does), the LString::Length() method is provided as a certain means of always obtaining the length of the string.

As an implementation note, there is only one data member in LString: mStringPtr. mStringPtr is a StringPtr which points to your Pascal-style string. Note that the actual storage for the string is not part of LString. Without this sort of design, neither TString nor LStringRef could exist (I'll discuss these classes later in the article). So does this mean that you always have to allocate your own storage for your string? Not at all, as this is the purpose of LStr255.

LStr255

LStr255 inherits from LString. You can find the declaration of LStr255 in LString.h and definition in LString.cp. If the name of the class looks familiar to you, this is intended. The design of LStr255 is to replicate the functionality of a Str255, but with lots of extras. The great part is an LStr255 can be used almost anywhere a regular Str255 is used and in the same manner. Furthermore an LStr255 inherits all of the functionality of LString, plus it provides the actual storage for the string itself (the mString data member) so you do not have to.

Listing 1: Use of Str255

Obtain and concatenate two strings
To explain a convention used in all listings: any function call
preceeded by the unary scope resolution operator (::) specifies
this symbol is located in the global namespace - the same place
where the Toolbox symbols are located. These are Toolbox calls.
Feel free to look them up in Inside Macintosh on the Apple WWW
site for more information and detail.

    // Declare a string and initialize it
    Str255    theString = "\pHello, World!";

    // Append another string, obtained from a STR# resource
    Str255 appendString;
    ::GetIndString(appendString, 128, 1);

    // Ensure we don't overrun our boundaries
    SInt16    charsToCopy = appendString[0];
    Size theStringSize = sizeof(theString);

    if ((theString[0] + charsToCopy) > (theStringSize - 1)) {
        charsToCopy = theStringSize - 1 - theString[0];
    }

    // Append the string
    ::BlockMoveData((Ptr)&appendString[1], theString +
                theString[0] + 1, charsToCopy);

    // Reset the length byte
    theString[0] += charsToCopy;

    // Draw the string
    ::DrawString(theString);

Listing 1 demonstrates a typical set of actions that one might perform on a string. All of that work and code just to concatenate two strings. Listing 2 shows the same code but written using LString. Don't worry if you do not understand everything that is going on in Listing 2 because I'll cover it all in coming sections.

Listing 2: Use of LStr255

Obtain and concatenate two strings

    // Declare a string and initialize it
    LStr255    theString( "\pHello World" );

    // Append another string, obtained from a STR# resource
    LStr255    appendString(128, 1);

    theString += appendString;

    // Draw the string
    ::DrawString(theString);

As you can see comparing Listing 2 to Listing 1, the same result is accomplished but Listing 2 contains a lot less code and is much easier to read. This just scratches the surface of what LString can do.

Two usage notes. First, when you are using LStr255 objects and viewing them in a debugger, you will actually see two instances of your string data within the LStr255 object. One of these will be from mStringPtr and the other from mString. Why do you see two strings and why are they always in sync? Why not just have one string? Remember that mStringPtr is just a pointer to your actual storage for your string, mString; rather, mStringPtr points to mString. This is how things are designed and it's all OK, don't worry about it. However if they are not in sync, you might want to investigate as this could be signaling a problem. Second, beginning with the next section and continuing for the remainder of the article, I will be using LStr255 objects in all code listings because LStr255 is the most commonly used type of LString. Do remember that what applies in those listings will generally apply to other LString derivatives as well, like TString and LStringRef.

TString

TString is a templatized version of LString. It allows you to utilize the power and functionality of LString upon any Pascal-style string type (Str255, Str63, Str15, etc.). Use of the class is exactly like using LStr255, except that you instantiate your object through normal C++ template instantiation mechanisms. Listing 3 is a rewrite of Listing 1 using TString. Note as well its similarity to Listing 2.

Listing 3: Use of TString

Obtain and concatenate two strings

    // Declare a string and initialize it
    TString<Str63>        theString("\pHello World");

    // Append another string, obtained from a STR# resource
    TString<Str255>        appendString(128,1);

    theString += appendString;

    // Draw the string
    ::DrawString(theString);

LStringRef

LStringRef is a newcomer to the LString family (it was introduced in PowerPlant v1.9.1, post CodeWarrior Pro 3). LStringRef provides you with a means to obtain the functionality of LString and use it to manipulate a string that you do not have ownership of. Unlike LStr255 and TString, LStringRef does not contain storage for the string itself; mStringPtr points to a string allocated elsewhere. For example, this can be used to change a string that is part of some data structure. Listing 4 demonstrates the use of LStringRef to change the filename in an FSSpec from whatever it originally was to "Hello File! copy".

Listing 4: Use of LStringRef

    // Obtain our file spec
    FSSpec        theFileSpec;
    theLFile->GetSpecifier(theFileSpec);

    // Create the LStringRef and have it point to the filename
    LStringRef        fileName(sizeof(theFileSpec.name),
                      theFileSpec.name);

    // Change the filename entirely
    fileName = "Hello File!";

    // Actually it's a copy
    fileName += " copy";

Basic Features

Now that you've been introduced to the LString family and seen a little of what LString can do, it's now time to dig through the lines of code in LString.cp and LString.h to really see all that LString has to offer. By the way, do make sure you also read through LString.h as many of LString's functions are declared inline in the header file.

Object Construction

If you look through the LString sources, you'll notice that there is only one LString constructor. That's all that LString needs as it performs the core setup of the class's functionality. Where the constructors are really needed is within the subclasses. Look at the number of constructors there are for LStr255! And look what you can do with all of those constructors as well. You can create your LStr255 from: another LStr255, another LString, a Pascal-style string, a C-style string, an arbitrary string of text, a single character, data within a Handle, from a 'STR ' or 'STR#' resource, from a long or short integer, a FourCharCode, and from floating point values (long double). Wow! That covers just about every and any way you might want to create your LStr255 object.

If you look at the implementation of most of those LStr255 constructors, you'll see many of them call the Assign() method. Assign() assigns the given value to the LString object. Just as the constructors use Assign(), you are free to use Assign() as well when you wish to assign a value to your LString object. Furthermore, as a logical use of C++, you can see in LString.h that the assignment operator (operator =) has been overloaded with just as many combinations for those of you that prefer the use of operators in logical situations. Listing 5 demonstrates the many ways Assign() can be used.

Listing 5: The Many Faces of Assign()

Each line group produces the same result of creating an LStr255
object and giving it a string of "Hello, Assign!".

    // Create and assign by initialization
    LSt255        initString("Hello, Assign!");
    // Create and assign by Assign()
    LStr255    assignString;
    assignString.Assign("\pHello, Assign!");
    // Create and assign by assignment
    LStr255    assignmentString = assignString;

String Manipulation

Now that you have created your LString and given it some text, you will probably want to do something with the text in that string. This functionality is what LString tends to be used for most, and within this subset lies the use of Append().

Append() appends a given value to an LString object. What can be appended is again just about anything (see LString.h for a complete list). As with Assign() and operator =, so with Append() and operator +=. You'll probably find yourself using operator += a great deal as it's so simple to type and makes your code so much easier to read. One issue to note with operator += (and perhaps other overloaded functions in LString) is that depending what you are trying to append you may receive compiler errors about an ambiguous access to an overloaded function. For instance, the following line of code will generate this error because the compiler cannot determine if you intend 1 to be treated as char, unsigned char, long, or short:

    theString += 1;

If you receive this compiler error, all you need to do to resolve it is apply a static_cast (or C-style cast) which will tell the compiler explicitly how you wish the value to be treated:

    theString += static_cast<SInt32>(1);

In addition to member functions for appending, LString also provides some global string addition operators (operator +). They are not LString member operators, but do act upon LString objects. These operators concatenate LString objects with: another LString object, a pointer to a string, or a character. The result of the addition is an LStr255 object. You can find these operators declared about mid-way through LString.h and defined near the bottom of LString.cp.

Finally, what would string manipulation be if you could not compare strings! You will find in LString.h a boat-load of global comparison operators that let you do just about any sort of string comparison you can think of: equals (operator ==), not equals (operator !=), greater than (operator >), less than (operator <), greater than or equals (operator >=), and less than or equals (operator <=). Listing 6 demonstrates the many ways you can manipulate strings with LString.

Listing 6: String Manipulation

    // Create our string, "Hi"
    LStr255    theString("Hi");

    // Whoops! We forgot the punctuation
    theString += '!';

    // Create a new string from 2 smaller strings

    LStr255    newString;
    newString = theString + "\p My name is John.";

    // Is our newString the same as our old string?
    // If so, beep.
    if ( newString == theString ) {
        ::SysBeep(2);
    }

Utilities

Rounding out the basic features of LString are some utility functions. They fall into two groups: object-related and public.

The object-related utilities are utilities that are class member functions and only behave in relation to their associated LString object. These are methods like: Length(), which returns the string length as an UInt8; ShortLength() which returns length as an SInt16; LongLength() which returns length as an SInt32 (the different return types are to ease places where you need the string length but do not wish to typecast to match a function parameter or avoid an implicit arithmetic conversion); GetMaxLength(), to return the maximum length the string can be; TextPtr() and ConstTextPtr() to return a Ptr (or const Ptr) to the raw text (avoids those ugly (Ptr)&theString[1] situations, as was used in Listing 1); operator [] to allow access to individual characters in the string, just like you could with a basic array. Listing 7a illustrates the use of these utilities.

Listing 7a: Utilities

    LStr255    theLStr255("\pYea");
    TString<Str255>    theTStr255; // LString::LString() properly
                                         // initializes to null.

    // Manually copy the text out of the LStr255 into a Str255
    ::BlockMoveData(    theLStr255.ConstTextPtr(),
                                    (Ptr)&theTStr255[1],
                                    theLStr255.LongLength()    );
    
    // Reset length byte
    theTStr255[0] = theLStr255.Length();

The public utilities of LString are a small group of static methods, publicly available for calling by pretty much anyone anywhere in your program (within reason, of course). These methods handle the most common string manipulations like: CopyPStr(), to copy one Pascal-style string into another Pascal-style string (e.g. Str255 to Str255); AppendPStr(), for a static version of LString::Append(); CStringLength() for obtaining the length of a C-style string with an upper limit of 255; FourCharCodeToPStr() and PStrToFourCharCode() for converting a FourCharCode (y'know, 'TEXT', 'PICT', 'PPob') to a Pascal-style string and back again; and FetchFloatFormat() and StringToLongDouble() to aid in some quick floating-point conversions. Listing 7b presents a display of these utilities' features.

Listing 7b: Static Utilities

    // Create a Pascal-style string and assign it a value
    Str255                herString;

    LString::CopyPStr("\pMary", herString);

    // Turn it into our app's signature
    FourCharCode    herSig;
    LString::PStrToFourCharCode(herString, herSig);

    // Clear original storage
    herString[0] = 0;

    // And back again
    LString::FourCharCodeToPStr(herSig, herString);

    // Add the rest
    LString::AppendPStr(herString, "\p Victoria");

Power Features

In addition to the basic functionality's discussed above, LString has some features for the power-user that help to round out the practicality of the class. If you consider yourself a beginner to LString, you should still give these power-features a look over as using these power-features is not difficult, and you can be a power-user in no time. The substring manipulations you may or may not use every day, but the "typecasting" abilities you'll find indispensable.

LString's ability to manipulate substrings provides you with the capability to perform substring searches, copies, and changes to that substring. In fact, you might even be able to create your own Find dialog using these features. The search functionality in LString allows you to: Find(), locate where a substring starts within the string; ReverseFind(), locate the position of a substring starting from the end of the string; to see if a string BeginsWith() or EndsWith() a specified string; find where the string starts within another string either from the beginning, FindWithin() or from the end, ReverseFindWithin(). In performing these searches, a comparison of text must of course be done. LString::ToolboxCompareText() is provided as the default mechanism for comparing the text; it uses the Toolbox CompareText() routine. If you do not wish to use the default comparison mechanism, you can specify your own CompareFunc via SetCompareFunc() to change it to whatever you would like. Of course after you have found your substring, you might want to do something with it. If you would like you can Insert() a string into your LString object, Remove() a substring from your object, or Replace() one substring with another. Or if you would just like to make a copy, operator () has been overloaded to provide you with an easy way to perform this copy. Listing 8 illustrates LString's substring manipulation features.

Listing 8: Substring Manipulation

    // Create the base string
    LStr255        theString("Get up. Stand up for your rights.");
    
    // Ensure the compare function is what we need
    theString.SetCompareFunc(LString::ToolboxCompareText);
    
    // Find a target string
    StringPtr    targetString = "\pStand up";
    UInt8            targetLength = targetString[0];
    
    UInt8    index = theString.Find( (Ptr)&targetString[1],
                                     targetLength );
    
    // Make a copy of the target string
    LStr255        targetCopy(theString(index,targetLength));
    targetCopy += "\p. ";

    // Insert the copy into the base string
    theString += char_Space;
    theString.Insert( targetCopy, index );

What I think is one of the niftiest features of LString is, as I like to call them, the typecasting operators. These are a series of operator overloads that fake the appearance of a typecast to allow your LString object to be flexibly used in many situations. You can find the operators listed near the top of the LString declaration in LString.h. These operators include: StringPtr, ConstStringPtr, SInt32, double, long double, and FourCharCode. When one of these operators is applied to the LString object, it converts the string into the "requested type" and returns the conversion. That is, apply operator SInt32 and the string is converted to a long integer and that long integer returned. The way to apply these operators is just like a typecast, but it's not really a typecast; it's technically an application of that operator, but the implementation and application of that operator simulates a typecast for ease of use and improved code readability. Furthermore, when attempting to resolve types, the compiler can implicitly apply these operators to the LString object for you. Look back at Figure 2 and notice that we passed our LStr255 object directly to DrawString? Listing 9 should clarify how all of this works.

Listing 9: Typecasting Operators

    // Create our string (FIXEDDECIMAL comes from fp.h)
    LStr255    theString(3.14, FIXEDDECIMAL, 2);

    // Convert it and do some math (the static_cast technically
    // isn't necessary, but here for illustration. Note that it
    // looks like a typecast, but if you watch the code execute
    // you'll step into operator double().
    double number = static_cast<double>(theString);
    number *= 2;

    // Convert back to a string. Note there is no operator =
    // for floating point variables. This is because of the
    // need for extra information.
    theString.Assign(number, FIXEDDECIMAL,2);

    // Draw onscreen. Note the implicit operator call to
    // operator StringPtr.
    ::DrawString(theString);

PowerPlant uses this technique of typecasting operators throughout the framework as a handy way to allow your objects to be treated as more basic Toolbox types for increased flexibility and seemlessness between PowerPlant, the Toolbox, and your code. See the use of operator Handle and operator Ptr in UMemoryMgr classes for another set of examples. Also, if you have access to the Scott Meyers book More Effective C++, read Item 5: Be wary of user-defined conversion functions. Scott labels the above technique implicit type conversion operators and points out the various strengths and weaknesses of the above technique.

Is There A Downside?

As great as LString is, there are certainly a few downsides. There may be others, but these are the big ones in my book. First, if you don't like Pascal-style strings or perhaps they're not as useful as a C-style string would be in a given situation, then of course LString would fall under the same roof. But if you use and/or like Pascal-style strings then this isn't much of an issue. Second, this code is C++. If you need Pascal-style string helper functions in C or another language then LString will not be of much use to you. Third, LString's are C++ objects. As an object it will have some additional overhead and memory requirements, at least compared to plain arrays (e.g. LStr255 vs. Str255). Furthermore because LString's are objects you cannot place them inside TArray's, except, as with all objects and TArray, as pointers to the object allocated via new. Due to this extra work, it's probably easier to just store plain Str255's in a TArray instead. But do not fret! If you must use Str255's you can still gain the features and power of LString via the LStringRef class applied to your Str255's.

Give It A Try

If you haven't already, give LString a try. The code listings should all function as compilable snippets. And since LString is mostly independent of the rest of PowerPlant (you may need to add the PP_Constants.cp file to your project as well), you can easily drop LString.cp into a basic Toolbox or console stationery and give them a try. Step through each listing in the debugger (make sure inlining is set to "Don't inline") and watch how things work. Watch where you go, how that pertains to the section material being discussed, and how they all fit into the larger picture. Just play around and experiment.

I hope that I've been able to give you a good introduction to the LString class and it's family of subclasses and utilities. It provides the Mac OS software developer with a fairly simple yet powerful class for working with Pascal-style strings. With the ability to create strings from almost any source, manipulate the contents of that string, and convert it for use in just about any situation, LString is a tool worth having in your programmer's toolbox.

I hope you find LString to be as ultra-groovy as I find it to be. But just in case, I'll borrow one from Dennis Miller: "Of course that's just me. I could be wrong."

Bibliography


John C. Daub can't think of anything snazzy to put in his bio other than 311's a great band to listen to while writing articles. If you'd like to contact John, you can do so at hsoi@metrowerks.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Bookends 12.5.8 - Reference management a...
Bookends is a full-featured bibliography/reference and information-management system for students and professionals. Access the power of Bookends directly from Mellel, Nisus Writer Pro, or MS Word (... Read more
Chromium 44.0.2403.125 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. Version 44.0.2403.125: This release contains a number... Read more
iMazing 1.2.2 - Complete iOS device mana...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
Audio Hijack 3.2.0 - Record and enhance...
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
FontExplorer X Pro 5.0.1 - Font manageme...
FontExplorer X Pro is optimized for professional use; it's the solution that gives you the power you need to manage all your fonts. Now you can more easily manage, activate and organize your... Read more
Calcbot 1.0.2 - Intelligent calculator a...
Calcbot is an intelligent calculator and unit converter for the rest of us. Featuring an easy-to-read history tape, expression view, intuitive conversion, and much more! Features History Tape -... Read more
MTR 5.0.0.1 - The Mac's oldest and...
MTR (was MacTheRipper)--the Mac's oldest and smartest DVD-backup app--is now updated to version 5.001 MTR -- the complete toolbox, not a one-trick, point-and-click extractor. MTR is intended for... Read more
LibreOffice 4.4.5.2 - Free, open-source...
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
Adobe Lightroom 6.1.1 - Import, develop,...
Adobe Lightroom is available as part of Adobe Creative Cloud for as little as $9.99/month bundled with Photoshop CC as part of the photography package. Lightroom 6 is also available for purchase as a... Read more
File Juicer 4.41 - Extract images, video...
File Juicer is a drag-and-drop can opener and data archaeologist. Its specialty is to find and extract images, video, audio, or text from files which are hard to open in other ways. It finds and... Read more

Pac-Man Championship Edition DX has brou...
Bandai Namco has released Pac-Man Championship Edition DX on iOS and Android, which features the classic arcade gameplay that we've all grown to love. Pac-Man Championship Edition DX can be enjoyed in much shorter bursts than the arcade versions... | Read more »
Cosmonautica (Games)
Cosmonautica 1.1 Device: iOS Universal Category: Games Price: $6.99, Version: 1.1 (iTunes) Description: Cast off! Are you ready for some hilarious adventures in outer space? | Read more »
Rescue humanity from a Demon horde in An...
Angel Stone is Fincon's follow up to the massively successful Hello Hero and is out now on iOS and Android. You play as a member of The Resistance, a group of mighty human warriors who have risen up in defiance of the Demon horde threatening to... | Read more »
Gallery Doctor (Photography)
Gallery Doctor 1.0 Device: iOS iPhone Category: Photography Price: $2.99, Version: 1.0 (iTunes) Description: Free up valuable iCloud and iPhone storage with Gallery Doctor, the only iPhone cleaner that automatically identifies the... | Read more »
You Against Me (Games)
You Against Me 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: A simple game… You. Me. Claim, steal, lock, score, win! | Read more »
Yep, it's True - Angry Birds 2 is O...
The not exactly rumors were true and the birds are back. Angry Birds 2 has come to the App Store and the world will... well I suppose it'll still be the same, but now we have more bird-flinging options! [Read more] | Read more »
You Could Design Your Own Card for Chain...
If you've ever wanted to create your own item, weapon, trap, or even monster for Chainsaw Warrior: Lords of the Night, this is your chance. Auroch Digital is currently holding a contest so that fans can fight to the death (not really) to see which... | Read more »
Bitcoin Billionaire is Going Back in Tim...
If you thought you managed to buy everything there is to buy in Bitcoin Billionaire and make all the money, well you though wrong. Those of you who made it far enough might remember investing in time travel - and it looks like that investment is... | Read more »
Domino Drop (Games)
Domino Drop 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Domino Drop is a delightful new puzzle game with dominos and gravity!Learn how to play it in a minute, master it day by day.Your... | Read more »
OPERATION DRACULA (Games)
OPERATION DRACULA 1.0.1 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0.1 (iTunes) Description: 25% off launch sale!!! 'Could prove to be one of the most accurate representations of the Japanese bullet hell shmup... | Read more »

Price Scanner via MacPrices.net

Sale! 13-inch MacBook Pros on sale for $100 o...
B&H Photo has 13″ MacBook Pros on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.5GHz/500GB MacBook Pro: $999.99 save $100 - 13″ 2.7GHz/128GB Retina... Read more
Sale! Save $100 on 13-inch MacBook Airs this...
B&H Photo has the 13″ 1.6GHz/128GB MacBook Air on sale for $899.99 including free shipping plus NY tax only. Their price is $100 off MSRP, and it’s the lowest price available for this model.... Read more
Worldwide Tablet Market Decline Continues, Ap...
The worldwide tablet market declined -7.0% year-over-year in the second quarter of 2015 (2Q15) with shipments totaling 44.7 million units according to preliminary data from the International Data... Read more
TP-LINK TL-PA8030P KIT Powerline Featuring Ho...
Consumer and business networking products provider TP-LINK is now shipping its TL-PA8030P KIT AV1200 3-Port Gigabit Passthrough Powerline Starter Kit that expands your home’s network over its... Read more
Apple refurbished iPad Air 2s available for u...
The Apple Store has Apple Certified Refurbished iPad Air 2s available for up to $140 off the price of new models. Apple’s one-year warranty is included with each model, and shipping is free: - 128GB... Read more
Updated Apple iPad Price Trackers
We’ve updated our iPad Air Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers. Read more
Apple refurbished 2014 13-inch 128GB MacBook...
The Apple Store has Apple Certified Refurbished 2014 13″ MacBook Airs available starting at $759. An Apple one-year warranty is included with each MacBook, and shipping is free: - 13″ 1.4GHz/128GB... Read more
Apple’s Education discount saves up to $300 o...
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
Save up to $600 with Apple refurbished Mac Pr...
The Apple Store has Apple Certified Refurbished Mac Pros available for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more
Mac Pros on sale for up to $260 off MSRP
B&H Photo has Mac Pros on sale for up to $260 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2799, $200 off MSRP - 3.5GHz 6-core Mac Pro: $3719.99... Read more

Jobs Board

*Apple* Certified Mac Technician - Updated 6...
…and friendly, hands-on technical support to customers troubleshooting and repairing Apple /Mac products with courtesy, speed and skill. Use your problem-solving skills Read more
Infrastructure Engineer - *Apple* /Mac Envir...
…a part of a team Requires proven problem solving skills Preferred Additional Apple Certified System Administrator (ACSA) Apple Certified Technical Coordinator (ACTC) 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
*Apple* Online Store UAT Lead - Apple (Unite...
**Job Summary** The Apple Online Store is a fast paced and ever evolving business environment. The User Acceptance Testing (UAT) lead in this organization is able to Read more
*Apple* MAC Support Services Subject Matter...
Title: Apple MAC Support Services Subject Matter Expert Location: Pleasanton, CA Type of position: Temporary Contract for approximately 6 weeks Tasks The tasks for the Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.