TweetFollow Us on Twitter

Uninstalling with AppleScript

Volume Number: 23 (2007)
Issue Number: 05
Column Tag: Scripting

Uninstalling with AppleScript

Building an uninstaller with AppleScript Studio

By José R.C. Cruz

Introduction

In a previous MacTech article, we learned how to use the PackageMaker tool to build a distribution package. We also learned how to localize that package for different languages, and how to customize it with scripts. But the one thing we were unable to do with the tool is to build an uninstaller.

Third-party tools such as ViseX and InstallerMaker can add an uninstall option to their installers. This feature is sadly missing from PackageMaker, though not without reason. In fact, most products are easy to remove -- they are contained in a single folder, and trashing that folder completely uninstalls the product. But some products will have multiple files installed in different directories. Others will create custom directories to store the files. A good example of such a product is Xcode. Its installer creates the custom directory Developer to contain its files. The installer also stores files in other directories such as /System/Library and /Library/Application Support. As a result, removing Xcode from the system can be a laborious process. In these situations, an uninstaller tool can be useful.

This article will demonstrate how to use AppleScript Studio to build an uninstaller. To get readers started on their own uninstallers, the Xcode project Uninstall is made available for downloading. A copy of this project can be obtained at the following URL: ftp://ftp.mactech.com/src/mactech/volume23_2007/23.05.sit.

The Receipts Bundle

After the Installer installs a software payload, it creates a copy of the package in the Receipts directory. The path to this directory is /Library/Receipts on the boot volume.

The copy of the installer package is known as a receipt bundle. Its presence indicates that a product has been installed successfully. It also tells the installer package if the latter has to do an upgrade, as opposed to an installation.

Figure 1 shows the internal structure of a typical receipt bundle. Notice that the bundle has most of the same files as an installer package. What is missing is the .paz.gz file containing the actual payload. Also missing are the two aliases to that file from the Resources subdirectory.


Figure 1. Structure of a typical receipts bundle.

There are software tools that purge the contents of the Receipts directory. This is often done to reclaim extra space, especially when the product associated with the receipt no longer exists. But removing a receipt, without removing the product, creates a new problem. Without the right receipt to guide it, an installer for a new product version may be unable to upgrade the current product correctly.

The BOM File

The BOM file is a list of all the files that comprise the software payload. It also defines the locations of each file on the target volume. This file is present in both the installer package and receipts bundle.

The BOM file format has its origins in the NeXTStep operating system. It is also a binary format and, as a result, is not directly readable. To read its contents, use the command-line tool lsbom to preprocess the BOM file.

The lsbom tool

The lsbom tool takes a BOM file as its input and renders its contents into human readable text. It then outputs the text to another file, or to stdout by default. The tool is a standard addition to the BSD subsystem of MacOS X. Its counterpart is the mkbom tool, which creates a BOM file for a given directory.

To use the lsbom tool, simply pass the path to the BOM file as its input. For example, to process the BOM file for the Sample.pkg receipt, type the following statement at the Terminal prompt.

   lsbom /Library/Receipts/Sample.pkg/Contents/Archive.bom

The tool will then parse the file and display its contents at lightning speed on the Terminal speed. To better read the output, pipe the results of the lsbom tool to the less tool.

   lsbom /Library/Receipts/Sample.pkg/Contents/Archive.bom | less

less will display the first N lines of text from lsbom. The number of lines displayed is dictated by the height of the Terminal window. To display the next N lines of text, tap on the Space bar. To display the previous N lines of text, tap on the B key while holding down the <CTRL> key.

Another way of handling the lsbom output is to save it to a file. To save the output to the file Sample.log, use the I/O redirection token '>'.

   lsbom /Library/Receipts/Sample.pkg/Contents/Archive.bom > Sample.log

Notice that, in both examples, the BOM file is always inside the Contents subdirectory of the receipt bundle. Also, the name of the BOM file is always Archive.bom. Most receipt bundles will follow the same conventions. Though it is possible for a BOM file to be located elsewhere in the bundle, this is rarely done.

The lsbom output

Listing 1 shows a sample output from the lsbom tool. Each entry corresponds to a file or directory installed by the package. The first three items of the BOM entry are arranged as follows.

directory_path file_modes user_id/group_id.

Each item is separated from the other by a single tab character (0x09). Now, if the entry is for a regular file, it will have two more items as shown below.

   directory_path file_modes user_id/group_id number_of_bytes crc_32

If it is for a symbolic link, its last item will be the path to the original file or directory.

   directory_path file_modes user_id/group_id number_of_bytes ¬
         original_path

Finally, if the entry is for a device file, its last item will be the assigned device number.

   directory_path file_modes user_id/group_id device_number

Listing 1. Sample output of the lsbom tool

.   40755   501/80
./Sample.app   40755   501/80
./Sample.app/Contents   40755   501/80
./Sample.app/Contents/Info.plist   100644   501/80   947 ¬
      1163649540
./Sample.app/Contents/MacOS   40755   501/80
./Sample.app/Contents/MacOS/Sample   100755   501/80   51956 ¬
      3193247409
./Sample.app/Contents/PkgInfo   100644   501/80   8 ¬
742937289
./Sample.app/Contents/Resources   40755   501/80
./Sample.app/Contents/Resources/Appearance.tiff   100644   501/80   4850 ¬
1276692542
...
./Sample.app/Contents/Resources/English.lproj   40755   501/80
./Sample.app/Contents/Resources/English.lproj/Credits.rtf   100644   501/80 ¬  
3163   3602318773
./Sample.app/Contents/Resources/English.lproj/Errors.strings   100644 ¬  
501/80   2978   2815210102
...

Most BOM file listings consist mostly of directories and regular files. Device files and symbolic links are seldom found. Also, the file_modes item is essentially the three permission flags written in octal form. Each of the lower three numbers represents the permission for world, group, and owner. The upper set of numbers represent the type of item in question. They are set to 40 for a directory, 100 for a generic file.

Notice that each directory_path item starts with a dot (.) character. This character is replaced by the IFPkgRelocatedPath value set in the Info.plist file. If that value is not set, the directory path is assumed to be relative to the OS X boot volume.

Also, if the package has installed payloads in other directories, the BOM listing will show these payloads. For example, if Sample.pkg has installed two files in the /usr/bin directory, the BOM listing may show these files as follows.

   ./usr/bin            40755      0/0
   ./usr/bin/foo      100755   0/0   12606   2275820725
   ./usr/bin/fubar      100755   0/0   12606   2275820725

Notice that both user_id and group_id items are set to 0 in the above example. This means that the owner of the two files and the directory is root. Removing the two files will require authentication. Do not, however, remove any directories or subdirectories with a root owner. Doing so may remove important files, and render the entire OS X platform unusable.

The lsbom options

The lsbom tool also provides a number of output options. Use these options to display specific entries from the BOM file as follows.

To display only the directories accessed or created during installation, use the -d option.

   lsbom -d /Library/Receipts/Sample.pkg/Contents/Archive.bom

The output listing will also include bundles such as .app, .bundle, and .lproj. To display only the paths of files that were installed or updated, use the -f option.

   lsbom -f /Library/Receipts/Sample.pkg/Contents/Archive.bom

To display only the paths of each directory and file, use the --s option.

   lsbom -s /Library/Receipts/Sample.pkg/Contents/Archive.bom

The lsbom tool also has options other than the ones shown above. To view a list of options, type the command lsbom -h at the Terminal prompt. Also, to view the tool's electronic manual, type info lsbom at the prompt.

AppleScript and the Shell

AppleScript is the native scripting language of the MacOS platform. First introduced in the 1990s, it is one of the first few languages that work in a GUI environment. It also uses a natural language syntax, which makes its scripts easy to read and write.

Another feature of AppleScript is that it can be extended using plug-ins. These plug-ins, or scripting additions, allow AppleScript to do tasks that are slow or impossible to do using the core language. The OS X version of AppleScript comes bundled with the plug-in named Standards Additions. With this plug-in, an AppleScript script can display simple dialogs and perform basic file I/O tasks. The script can also run Unix shell scripts using the do shell script function.

,b>The do shell script function

The do shell script function is AppleScript's gateway to the BSD subsystem of MacOS X. With this function, an AppleScript script can execute command-line tools or shell script files. It can also run a single-line shell script using this function. The function returns any results from the script as a string.

The function uses the interpreter set by the SHELL environment variable to do its tasks. To find out the current interpreter, launch the Script Editor tool, which is located in /Applications/AppleScripts. On the script window, type do shell script "printenv SHELL" and click on the Run button. If the current interpreter is bash, the function will return the string value of SHELL=/bin/bash on the Results pane.

Working with file paths

When using the do shell script function to manipulate files, it requires the file paths expressed using the POSIX format. In short, a forward slash character </> must separate each path name. For example, to parse the BOM file for Sample.pkg, pass the script to the function as follows.

   do shell script ¬
      "lsbom /Library/Receipts/Sample.pkg/Contents/Archive.bom"

Now if a path name contains any spaces, a reverse slash <\> character must precede each space. For example, to parse the BOM file for Test Sample.pkg, pass the script as follows.

   do shell script ¬
      "lsbom /Library/Receipts/Test\\ Sample.pkg/Contents/Archive.bom"

Notice that two reverse slashes precede the space in Test Sample.pkg. This is necessary due to a little quirk in AppleScript. The first reverse slash tells AppleScript to treat the second slash is part of the string. The second slash tells the shell interpreter to treat the space as part of the script text.

AppleScript, however, expresses its file paths using the MacOS format. Instead of a forward slash, each path name is separated by a colon <:> character. Also, each path name can contain spaces without the need for any reverse slashes. For example, the file path to the BOM file for Test Sample.pkg is written in MacOS format as follows.

   OS X:Library:Receipts:Test Sample.pkg:Contents:Archive.bom

Converting between file path formats can be quite tedious. To address this issue, the Standards Additions plug-in provides the POSIX file class. To convert the MacOS file path to Sample.pkg to the POSIX format, type the following statement in the Script Editor window.

   POSIX path of alias "OS X:Library:Receipts:Sample.pkg"

This will return the converted path as /Library/Receipts/Sample.pkg. To convert it back to a MacOS format, type the following on the editor window.

   POSIX file "/Library/Receipts/Sample.pkg"

Both examples have file paths set relative to the boot volume. Both also assume that the MacOS name of the boot volume is OS X. Now if a file path is set relative to a volume other than boot, the conversion will reflect that volume. For example, if the MacOS file path is set to Users:Applications:Public:, it will be /Volumes/Users/Applications/Public/ in POSIX format.

Authenticating a command

Some shell commands require authentication in order to perform their tasks. They are usually invoked in the Terminal window using the sudo command. For example, to create the subdirectory foo in /usr, type the following line at the Terminal prompt.

   sudo mkdir /usr/foo

The sudo command first prompts the user for an administrative password. When the correct password is entered, sudo then executes the mkdir command. Otherwise, it aborts after the user fails to enter the right password thrice in a row.

Using the sudo command through the do shell script function is both tedious and unnecessary. Instead, the function can authenticate the desired command by itself. For example, to create the same subdirectory shown above, type the following line on the Script Editor window.

   do shell script "mkdir /usr/foo" with administrator privileges

The function first prompts the user for a password using the dialog shown in Figure 2. Again, when the user enters the correct username and password, the function then executes the mkdir command. Otherwise, it aborts with an error message after the user fails to enter the right information three times in a row. The same also happens if the user clicks on the Cancel button.


Figure 2. The authentication dialog.

To use either approach, make sure that you have a user account with administrative privileges. To learn how to create such an account, consult one of the references listed at the end of this article.

Building with AppleScript Studio

There are many ways to build an uninstaller. One way is to build it as a shell script. One example is Xcode, which comes with a Perl script to uninstall its various components. This approach is easy to implement and test. It does, however, require the use of the Terminal window. It also provides very poor user interaction and feedback, if any.

Another way is to build the uninstaller as a Cocoa application. Cocoa gives the uninstaller a better way of interacting with the user. It also allows the uninstaller to perform tasks not possible with a shell command. But this approach requires too much resources and time to implement. It also has a very high learning curve.

A more practical way is to build the uninstaller as an AppleScript application. This is now easy to do with AppleScript Studio. The uninstaller gets a decent interface with which to interact with the user. It will be easy to build and test due to AppleScript's user-friendly syntax. This is the approach used for the Xcode project Uninstall.

Laying out the user interface

The Uninstall project has a single main window named Uninstall Demo. The window is subdivided into two panels by an NSTabView control. Both panels contain a single NSTableView control. The first table displays the contents of the Receipts directory (Figure 3). The second table displays the contents of the BOM file for the selected receipt (Figure 4).


Figure 3. The Uninstall window, Receipts panel.


Figure 4. The Uninstall window, Files panel.

The entire window layout is that of a basic Assistant. On the lower right corner are two pushbuttons, Prev and Next. The Next button is also set as the default button.

Both buttons are disabled by default. The Next button is enabled when the Receipts table has a selected entry. The Prev button is enabled when the current panel is the one with the Files table. Also, when the Files table has a selected entry, the Next button becomes the Uninstall button.

On the lower left corner of the window is the Cancel button. This button is set to respond to the <Esc> key. It also sends a performClose: message to the window when clicked.

Binding the widgets

The window and some of the controls are then bound to specific AppleScript handlers. The bindings are set in the AppleScript panel of the Show Inspector dialog (Figure 5). To display the dialog, choose Show Inspector from the Tools menu. Then select AppleScript from the drop-down list at the top of the dialog.


Figure 5. The Show Inspector dialog, AppleScript panel.

Table 1 is a list of the bindings set for each interface widget. The handlers shown in this table are all defined in the source file Uninstall.applescript. Notice that some widgets are bound to the same handlers. To identify which widget called the handler, check its name property. For instance, the following code fragment shows how to determine which button was clicked.

on clicked theObject
   local tBtn
   
   set tBtn to the (name of theObject) as string
   if (tBtn is equal to "prev") then
      -- the Previous button has been clicked
   else if (tBtn is equal to "next") then
      -- the Next button has been clicked
   end if -- (tBtn is equal to "prev")
end clicked -- theObject

Widget Name Class AppleScript Settings
Name Event Handler
Uninstall Demo NSWindow demo Nib awake from nib
Prev NSButton prev Action clicked
Next NSButton image/next Action clicked
Receipts NSTableView rcpt Nib awake from nib
Data View selection changed
Lists NSTableView list Nib awake from nib
Data View selection changed

Table 1. AppleScript settings for the Uninstall UI widgets.

Not shown in the table are the bindings for the application itself. To bind the application, select the File's Owner icon on the MainMenu.nib window. Then, from the Show Inspector dialog, click on the Application checkbox. Then set the bindings as shown in Figure 6.


Figure 6. Binding the AppleScript application.

Binding the Cancel button

On the other hand, the Cancel button is not bound to any AppleScript handler. Instead, it is bound directly to an action handler.

To set the binding, control-drag a line from the button to the main window. This will display the Show Inspector dialog with the Connections panel view active. Click on the Target/Action tab on the dialog. Then choose performClose: from the list of window actions (Figure 7).


Figure 7. Selecting the performClose: action.

The Quit Uninstall menu item is also bound in the same way. But this will be left as an exercise to the readers.

Building with Xcode

The Xcode project Uninstall contains three AppleScript source files. The Uninstall.applescript file has all the handlers called by the UI widgets. The Receipts.applescript file has the code to access the Receipts directory. Finally, the Files.applescript file has the code for processing the BOM file. It also has the code that will do the uninstall task.

For reasons of length, this article will only show code that is relevant and interesting. Readers can always view the entire source files by downloading the project from the MacTech website.

Accessing the Receipts directory

Shown in Listing 2 is the function handler that retrieves the contents of the Receipts directory. It takes a path to the directory as its input argument. It returns the results of the retrieval as a list of records.

First, the handler calls the list folder function to read the directory contents. The function responds by returning the contents as a list of filenames. Next, the handler parses each filename in the list. If the name belongs to a receipt bundle, the handler retrieves its bundle signature. Otherwise, the handler proceeds to the next name.

The handler uses the bundle signature to create a record together with the name. When done, it appends the record to the return list tPkg.

Listing 2. Retrieving a list of receipt bundles (Receipts.applescript).

property pRcptRec : {bnom:"", bsig:""}
to getBundles from aPath
   local tLst, tPkgs
   local tItem, tNom
   local isPkg
   
   -- read the contents of the directory
   set tLst to (list folder aPath)
   
   -- parse the list results
   set tPkgs to {}
   copy pRcptRec to tRec
   
   repeat with tNom in tLst
      -- prepare a path to a list item
      set tItem to aPath & ":" & tNom
      get info for file tItem
      
      -- is the item a bundle?
      set isPkg to package folder of result
      
      if (isPkg) then
         -- retrieve the bundle signature
         set tSig to (getBundleSignature for tItem)
         
         -- update the record template
         set bnom of tRec to tNom
         set bsig of tRec to tSig
         
         -- add the updated record to the list
         copy tRec to the end of tPkgs
      end if -- (isPkg)
   end repeat -- with tNom in tLst
   
   -- return the retrieval results
   return (tPkgs)
end getBundles -- from aPath

Reading the BOM file

Listing 3 shows the function handler used to convert the BOM file. It also shows how to use the do shell script function to call the lsbom tool. The handler takes a path to the receipt bundle as its input argument. If successful, it returns the file path to a temp file; otherwise, it returns an empty string.

First, the handler gets a path to the TemporaryItems directory. It converts the path to a POSIX formant, and appends the name of the temp file bom.out. This file will store the output results of the lsbom tool.

The handler then prepares the script to be executed with the do shell script function. The script consists of the file path to the BOM file, as well as the path to bom.out. For example, if the target receipt is Sample.pkg, the script will read as follows.

   lsbom --p fs /Library/Receipts/Sample.pkg/Contents/Archive.bom ¬ 
               > ~/Library/TemporaryFiles/bom.out

Note that, in actual practice, the entire script will consists only of a single line. It will also state the full path to the bom.out file on the user home directory.

Notice as well that a --p fs option is passed to the lsbom tool. This option tells the tool to display only the file paths and sizes of each BOM item. If the item happens to be a directory, its entry in the bom.out file will not have any size data.

Listing 3. Generating a BOM file for a given receipt (Files.applescript).

property pBOMPath : "/Contents/Archive.bom"
property pBOMTemp : "bom.out"
property pCmd : "lsbom "
property pOpts : "-p fs "
to getBOM for aRcpt
   local tTmp, tCmd
   
   -- retrieve a path to a temp directory
   set tTmp to path to temporary items from user domain
   set tTmp to POSIX path of tTmp
   
   -- prepare the output file path
   set tTmp to tTmp & "/" & pBOMTemp
   
   -- prepare the shell command
   set tCmd to pCmd & pOpts
   set tCmd to tCmd & aRcpt & pBOMPath
   set tCmd to tCmd & " > " & tTmp
   
   -- execute the shell command
   do shell script tCmd
   
   -- was it successful?
   try
      set tTmp to (POSIX file tTmp) as string
      alias tTmp
      return (tTmp)
   on error tErr number tTyp
      display dialog ("[FATAL]" & tErr as string)
      return ("")
   end try
end getBOM -- for aRcpt

Listing 4 shows the function handler used to parse the contents of the bom.out file. It takes the path to that file as its input argument. When done, it returns a list of records, each record representing a BOM entry.

The handler first opens a read-only access to the bom.out file. It reads all the entries in the file, and then closes the access.

The entries consist of a list of strings. Each entry alternates between the file path and size of a BOM item. The handler creates a BOM record for each entry. It then appends the record to the list variable tBOM, which is returned to the calling handler.

Listing 4. Reading the BOM file (Files.applescript).

property pBOMRec : {fnom:"", fsiz:""}
property pLF : 10
property pHT : 9
to loadBOMItems from theFile
   local tSrc, tSiz, tPos, tLen
   local tLst, tBOM, tDat, tRec, tNom
   local tTkn, tOdd
   
   -- initialize the following locals
   set tBOM to {}
   set tTkn to {}
   set tTkn to tTkn & (ASCII character pLF)
   set tTkn to tTkn & (ASCII character pHT)
   
   try
      -- start a read-only access to the file
      open for access theFile without write permission
      set tSrc to result
      if (tSrc > 0) then
         -- read the contents of the file
         read tSrc using delimiter tTkn
         set tLst to result
         
         -- close the read-only access to the file
         close access tSrc
      end if -- (tSrc > 0)
      
      -- parse the BOM entries
      copy pBOMRec to tRec
      set tLen to the length of tLst
      
      if (tLen > 0) then
         repeat with tPos from 1 to tLen by 2
            -- retrieve the following BOM items
            set tNom to item tPos of tLst
            set tSiz to item (tPos + 1) of tLst
            if (tSiz is equal to "") then
               set tSiz to "-1"
            end if -- (tSiz is equal to "")
            
            -- prepare the BOM record
            set fnom of tRec to (tNom as string)
            set fsiz of tRec to (tSiz as string)
            
            -- append the record to the return list
            copy tRec to the end of tBOM
         end repeat -- with tPos from 1 to tLen by 2
         
         -- remove the first two items
         set tLen to length of tBOM
         set tBOM to items 3 thru tLen of tBOM
end if -- (tSiz > 0)
   on error tErr
      -- something wrong has happened
      display dialog ("[FATAL] loadBOMItems:" & tErr as string)
   end try
   
   -- return a list of BOM entries
   return (tBOM)
end loadBOMItems -- theFile

Removing a BOM item

There are a many ways to remove a software product. The direct way is to locate the topmost directory from the BOM, and delete it together with its contents. Another way is to select specific items from the BOM for deletion. Choosing the right approach depends on the aim of the uninstaller. The Uninstall project, for instance, uses a variant of the second approach.

Shown in Listing 5 is the function handler that will remove a BOM item. It is called after the user selected an item from the Files listbox, and clicked on the Uninstall button.

The handler first creates an Uninstall folder in the TemporaryItems directory. Next, it moves the BOM item from its original path to the folder. It also deletes the item at the specified path. Once the handler completes its task, it returns a true to the calling handler. Otherwise, it returns a false if any errors occurred.

Listing 5. Removing a BOM item (Files.applescript).

   property pDirTrash : "Uninstall"
   to removeTheItem given path:aTgt, folder:aDir
      local tTmp, tBin, tCmd
      
      -- initialize the following locals
      set tTmp to path to temporary items from user domain as string
      set tBin to tTmp & pDirTrash
      
      -- create the temporary uninstall directory
      tell application "Finder"
         try
            if not (exists alias tBin) then
               make new folder at folder tTmp ¬
                  with properties {name:"Uninstall"}
            end if -- (exists alias tBin)
         on error tErr
            display dialog ¬
               "[FATAL] Failed to create the Uninstall directory"
            return false
         end try
      end tell -- application "Finder"
      
      -- attempt to remove the item
      tell application "Finder"
         try
            -- is the item a directory or a file?
            if (aDir) then -- it is a directory
               -- test delete the directory
               move folder aTgt to folder tBin with replacing
               delete folder aTgt
            else -- it is a file
               -- test delete the file
               move file aTgt to folder tBin with replacing
               delete file aTgt
            end if -- (aDir)
         on error tErr
            display dialog "[ERROR] " & tErr
            return false
         end try
      end tell -- application "Finder"
      
      -- the removal was successful
      return (true)
   end removeTheItem -- given path:aTgt, type:aTyp

Notice that the handler uses the Finder to delete the BOM item. While this works in most cases, it will fail if the item is inside a restricted directory such as /usr. For that case, replace the delete code with the following script statements.

   set aTgt to the POSIX path of alias aTgt
   set tCmd to "rm -Rf " & aTgt
   do shell script tCmd with administrator privileges

The above statements will prompt the user to validate the deletion that is about to occur.

Also, notice that the handler first makes a copy of the BOM item it is about to delete. This gives the user a chance to restore the deleted item back to its former location.

Final Thoughts

Product removal is just as important as product installation. Though most products are easy to remove manually, some require the use of an uninstaller tool. The tool will peruse the receipt package for the product, and delete all the files that belong to that product. This will help ensure that future products installations will be more successful.

AppleScript Studio makes it quite easy to build an uninstaller tool. It has a much lower learning curve compared to Cocoa. This alone makes for a faster build and deployment cycle. It allows the addition of a user-friendly interface, which is not possible through shell scripts.

Hopefully, this article helps to get you started in writing your own uninstaller. Until Apple adds an uninstall option to the Installer tool, writing your own is, for now, the next best solution.

Bibliography and References

Apple Computers. AppleScript Resources. Retrieved 2007 Feb 24. Online: http://www.apple.com/applescript/resources.

Apple Computers. "do shell script in AppleScript". Technical Note TN2065. Copyright 2003, 2005, 2006. Apple Computers, Inc.

Apple Computers. "Administrative Accounts". An Introduction to MacOS X Security for Web Developers. Copyright 2007. Apple Computers, Inc. 2004 Aug 25. Online: http://developer.apple.com/internet/security/securityintro.html

Apple Computers. "lsbom -- list contents of a bom file". Mac OS X Man Pages. Copyright 2003. Apple Computers, Inc. 2003 Apr 16. Online: http://developer.apple.com/documentation/Darwin/Reference/Manpages/man8/lsbom.8.html.


JC is a freelance engineering consultant and writer currently residing in North Vancouver, British Columbia. He divides his time between writing technical articles, and teaching origami at his local district's public library. He can be reached at anarakisware@cashette.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.