 |

 |
| |
 |
 |
 |
 |
|
 |
 |
When we left off last time, we had finished up with creating a brand new file, grabbing data for it, formatting the data into a line of tab-delimited text, and then writing that to the file. Now, we're going to append data to an existing file, but first, I made some changes to the creating a new file code. Since this is tab-delimited text, it's reasonable to assume that it's going to get used in a spreadsheet application of some kind. Well, you'd be assuming, not me, since that's why I wrote it that way. However, I realized that header rows don't suck, so I created one of those in the new file, so that when you open it in something like Numbers or Excel, you get a nice header row that explains what each row does. I'm not going to put up the entire function again, just the new lines: set theTempString to "Time" & " " & "SSID" & " " & "Authmode" & " " & "Channel" &
" " & "Data Rate (Mbps)" & " " & "Signal Strength (dbm)" & " " & "Signal Noise (dbm)" & " " & "MAC of WAP" & " "
--createt the header line for the file
set theFileString to my NSString's stringWithString_(theTempString)
--AppleScript string to NSString
set theFileData to theFileString's dataUsingEncoding_(NSUTF8StringEncoding of current application)
--NSString to NSData
theFileHandle's writeData_(theFileData)
--Write the header to the new file
set theFileLengthPointer to theFileHandle's seekToEndOfFile()
--move the file pointer to the end of this string
--since we KNOW what created this line, we don't have to be paranoid, like we are
--with appending data The first few lines are nothing major. We create a string with the column headers ended by a return, and shove that into an NSString. (We could save a line of code and just put the raw string in the stringWithString_() method, but this way is neater to read. The new stuff is the set theFileLengthPointer to theFileHandle's seekToEndOfFile() line. What this does is move the "start writing data here" pointer to the end of the file, so that the next time we write data to that file, it's going to start in the right place. We use this in the appending function too: on appendToExistingFile_(sender)
set theOpenPanel to my NSOpenPanel's openPanel()
--create an open panel
theOpenPanel's setCanChooseFiles_(1)
--the user can choose files
theOpenPanel's setResolvesAliases_(1)
--it will resolve aliases to their original file
theOpenPanel's setCanChooseDirectories_(0)
--the user cannot chose a directory, it makes no sense in this application's context
theOpenPanel's setAllowsMultipleSelection_(0)
--the user cannot choose multiple files, again, not appropriate for the application's context
set theOpenPanelResult to theOpenPanel's runModal()
--run the Open panel
if theOpenPanelResult is 1 then --if the user clicked okay
my appendToExistingDataButton's setEnabled_(false)
--disable the button to append to an existing data file
--we only work on one file at a time.
my createNewDataButton's setEnabled_(false)
--disable the button to create a new data file
--we're appending to a file, letting the user create a new one
--while this is going on would be dumb
set theDataFilePath to first item of theOpenPanel's filenames()
--get the file paths and names, in case we need them
--this returns an array with a single item, so we use that
set theDataFileURL to the first item of theOpenPanel's URLs()
--get the URL to the file, as that's what we should be using
--since this returns an array/list, we have to get the first item of that list
set theFileHandle to my NSFileHandle's fileHandleForWritingToURL_error_(theDataFileURL, missing value)
--create the file handle
set theFileLengthPointer to theFileHandle's seekToEndOfFile()
--set the file handle pointer to the current end of the file
set theFileString to my NSString's stringWithString_(" ")
--we're going to write a single blank line just to be sure we aren't jamming on the end of the data.
--We don't know for sure what created this original file, so better an extra blank line than mangled data.
set theFileData to theFileString's dataUsingEncoding_(NSUTF8StringEncoding of current application)
theFileHandle's writeData_(theFileData)
set theFileLengthPointer to theFileHandle's seekToEndOfFile()
--seek to new end of file
set createDataFile to false
--make sure this flag is false
set appendToExisting to true
--we need this to be true
end if end appendToExistingFile_This code really works like the new file code with a few exceptions. Obviously we're not using the save panel, since the file already exists, so for this, we use NSOpenPanel instead. We set a few options, like allowing the user to choose files, resolving aliases if one is chosen, and disabling multiple files, and choosing directories, as neither of those make sense in this case. Once that's done, we use the open panel's runModal() to display the panel. If the user picks a file and hits okay, then we disable append and create new file buttons in the App UI, since we don't want another file being used while we're writing to this one. Like we did with the save panel, we grab both the path and the URL, although unlike the new file code, we don't actually need the path. Also, unlike the save panel, NSOpenPanel returns us an array, or AppleScript list, not a text string. Since we need the text string, we set the file path and url variables to the first item of the array (Since we only allow the user to pick a single item, this assumption is fairly safe.): set theDataFilePath to first item of theOpenPanel's filenames() set theDataFileURL to the first item of theOpenPanel's URLs()Next, we get the file handle for the file at the end of the URL via fileHandleForWritingToURL(), which gives us a file handle for the file we're going to be appending data to. Since we aren't dealing with error handling, we just pass nil or missing value to the method for the error parameter. Now, we're appending data to the end of an existing file, so we want to make sure we're starting at the end of the file. Like we did in the new code for the new file code, we use seekToEndOfFile() to make sure we're starting at the end of the file. (Yes, as someone used to being far more explicit with such things in AppleScript, the 'trust me, the pointer's in the right place' thing going on here was a little disconcerting, but it obviously works, so...) Next, just because I want to be REALLY SURE that we can clearly tell where the data we're appending starts, (I'm paranoid, sue me), we write a single return to the file, then call seekToEndOfFile() again. So now we're at the end of a file, and we should have a blank line between the new and the old data. We also make sure the createDataFile flag is false, and the appendToExisting flag is true. Once that's taken care of, we're done with this function, and we move on to the last of the new code for appending files in the loadData() handler. This code is, as you can see, pretty much like the new file code: if (appendToExisting is true) and (theSaveFileFlag is true) then --both have to be true
set theTempString to (theTime's stringValue() as text) & " " & (currentSSID's stringValue() as text) &
" " & (authMode's stringValue() as text) & " " & (currentChannel's stringValue() as text) &
" " & (currentDataRate's stringValue() as text) & " " & (signalStrength's stringValue() as text) &
" " & (signalNoise's stringValue() as text) & " " & (currentWAPMAC's stringValue() as text) & " "
set theFileString to my NSString's stringWithString_(theTempString)
--convert the AppleScript string to NSString
set theFileData to theFileString's dataUsingEncoding_(NSUTF8StringEncoding of current application)
--NSString to NSData
theFileHandle's writeData_(theFileData)
--write us some data end ifThe cleanup code when you're done writing is the same code as the new file code, so we don't have to go over it again. The next update may be a while, I'm trying to dig up a way to do the signal vs. noise charts that will display directly in the application.
|
 |
 |
 |
 |
|
 |
 |

 |
| |
 |
 |
 |
 |
|
 |
 |
What AppleScript Programmers have been waiting for, even if they didn't know it: AppleScriptObjC
As some of you might know, AppleScript Studio, (Studio for short), never impressed me. To be blunt, I thought that it was a nice try that quickly showed itself to have gone down a bad path. The problem was that to do anything but a really narrow range of tasks (for Cocoa anyway), you had to use the "call method" command with a gob of 'real' Cocoa code, and if you're going to do that, why bother with Studio? The debugger was essentially unusable, and much of the time, just rebuilding a project with no code changes whatsoever would fail. I have a real limit as to how much work I'll do to make up for a bad tool, and that's what Studio was...a bad tool with good intentions. Especially because I'd been spoiled by really good AppleScript tools like Script Debugger. Compared to Script Debugger, Studio lived down to its unfortunate acronym. However, while no one was looking, Apple was listening. With Mac OS X 10.6, they released AppleScriptObjC, which is finally the product that Studio never could be: A real, first-class way to use AppleScript to create Cocoa applications. No more half-baked implementations or "oh, you need call method to do THAT" nonsense. Access to all the frameworks now, and in the future, the same way that Cocoa developers using Objective-C, Ruby, or Python get them...for free. I'm not going to do a tutorial on AppleScriptObjC, because I'm still wrapping my head around it. If you want a tutorial, there's a good one at MacScripter. What I am going to do is do a light comparison between an AppleScriptObjC application, and the Objective-C version, then one between an AppleScriptObjC application and the Studio version. As you'll see, AppleScriptObjC is a win. One of the things that AppleScriptObjC does is use AppleScript in a more "Cocoa-y" fashion. That is, Apple made the decision that for AppleScriptObjC, the way you use AppleScript should match the way you'd use other languages. There are obviously going to be significant differences between Objective-C, Ruby, Python, and AppleScript, but in general, Apple tried to make using AppleScriptObjC 'feel' as close to using any other language as possible. (There are more details on this in the AppleScriptObjC release notes I linked to.) DotViewDotView is a simple application that draws a solid circle, or dot, in a window. You can move the dot around with the mouse, there's a slider to change the dot size, and a color control to change the dot's color. It's a simple application, and so is a good way to show the similarities between Objective-C in Cocoa and AppleScript in AppleScriptObjC. DotViewLooking at the Objective-C version, there's not a lot of code. There's two .m files, DotView.m and main.m, and two header files, DotView.h and Preview.h. We'll focus on just the DotView.* files here, starting with the Objective-C version of DotView.h <#import Cocoa/Cocoa.h>
@interface DotView : NSView { NSPoint center; NSColor *color; CGFloat radius; }
// Standard view create/free methods - (id)initWithFrame:(NSRect)frame; - (void)dealloc;
// Drawing - (void)drawRect:(NSRect)rect; - (BOOL)isOpaque;
// Event handling - (void)mouseUp:(NSEvent *)event;
// Custom methods for actions this view implements - (IBAction)setRadius:(id)sender; - (IBAction)setColor:(id)sender;
@end
Not much really, about 14 lines of code that does the setup for things like setting the center, color, and radius of the dot, the functions for drawing the dot, and the functions for resizing and changing the color of the dot. As far as the AppleScriptObjC version goes, there isn't one. AppleScript is a higher level language, and doesn't use header files. The AppleScriptObjC version still has to deal with the items that are in the Objective-C header file, but it will do so differently. (note that in this usage, "higher" is not a sign of superiority or inferiority. Instead, it denotes how "far from the hardware" a language lives. So AppleScript is a higher level language than Objective-C which is a higher level language than Assembly.) What about the 'real' code? Well, here's the Objective-C DotView.m contents: #import <Cocoa/Cocoa.h> #import "DotView.h"
@implementation DotView
- (id)initWithFrame:(NSRect)frame { [super initWithFrame:frame]; center.x = 50.0; center.y = 50.0; radius = 10.0; color = [[NSColor redColor] retain]; return self; }
- (void)dealloc { [color release]; [super dealloc]; } // drawRect: should be overridden in subclassers of NSView to do necessary // drawing in order to recreate the the look of the view. It will be called // to draw the whole view or parts of it (pay attention the rect argument); // it will also be called during printing if your app is set up to print. // In DotView we first clear the view to white, then draw the dot at its // current location and size.
- (void)drawRect:(NSRect)rect { NSRect dotRect;
[[NSColor whiteColor] set]; NSRectFill([self bounds]); // Equiv to [[NSBezierPath bezierPathWithRect:[self bounds]] fill]
dotRect.origin.x = center.x - radius; dotRect.origin.y = center.y - radius; dotRect.size.width = 2 * radius; dotRect.size.height = 2 * radius; [color set]; [[NSBezierPath bezierPathWithOvalInRect:dotRect] fill]; }
- (BOOL)isOpaque { return YES; }
// Recommended way to handle events is to override NSResponder (superclass // of NSView) methods in the NSView subclass. One such method is mouseUp:. // These methods get the event as the argument. The event has the mouse // location in window coordinates; use convertPoint:fromView: (with "nil" // as the view argument) to convert this point to local view coordinates. // // Note that once we get the new center, we call setNeedsDisplay:YES to // mark that the view needs to be redisplayed (which is done automatically // by the AppKit).
- (void)mouseUp:(NSEvent *)event { NSPoint eventLocation = [event locationInWindow]; center = [self convertPoint:eventLocation fromView:nil]; [self setNeedsDisplay:YES]; }
// setRadius: is an action method which lets you change the radius of the dot. // We assume the sender is a control capable of returning a floating point // number; so we ask for it's value, and mark the view as needing to be // redisplayed. A possible optimization is to check to see if the old and // new value is the same, and not do anything if so.
- (void)setRadius:(id)sender { radius = [sender doubleValue]; [self setNeedsDisplay:YES]; }
// setColor: is an action method which lets you change the color of the dot. // We assume the sender is a control capable of returning a color (NSColorWell // can do this). We get the value, release the previous color, and mark the // view as needing to be redisplayed. A possible optimization is to check to // see if the old and new value is the same, and not do anything if so. - (void)setColor:(id)sender { [color autorelease]; color = [[sender color] retain]; [self setNeedsDisplay:YES]; }
@end
I left some of the functional comments in, so it's easier to see what the different parts of DotView.m are doing, but if you remove the comments and blank lines, the entire 'main' part of the application is only 47 lines of code. Admittedly, it doesn't do a lot, but still, that's not a lot. What about the AppleScriptObjC version? Here: property NSColor : class "NSColor" property NSBezierPath : class "NSBezierPath"
script DotView property parent : class "NSView" property |center| : {x:0.0, y:0.0} property radius : 0.0 property |color| : missing value on initWithFrame_(frame) continue initWithFrame_(frame) set my |center|'s x to 50.0 set my |center|'s y to 50.0 set my radius to 10.0 set my |color| to NSColor's redColor() return me end initWithFrame_ on drawRect_(rect) NSColor's whiteColor()'s |set|() tell NSBezierPath's bezierPathWithRect_(my |bounds|()) to fill() set origin to {(my |center|'s x) - (my radius), (my |center|'s y) - (my radius)} set |size| to {2 * (my radius), 2 * (my radius)} my |color|'s |set|() tell NSBezierPath's bezierPathWithOvalInRect_({origin, |size|}) to fill() end drawRect_ on isOpaque() return true end isOpaque on mouseUp_(|event|) set eventLocation to |event|'s locationInWindow() set my |center| to my convertPoint_fromView_(eventLocation, missing value) tell me to setNeedsDisplay_(true) end mouseUp_ on setRadius_(sender) set radius to sender's doubleValue() tell me to setNeedsDisplay_(true) end setRadius_ on setColor_(sender) set my |color| to sender's |color|() tell me to setNeedsDisplay_(true) end setColor_ end script
This does the same thing as the Objective-C version, but in about 40 lines of code. If that doesn't make any sense, this is where AppleScript being a higher level language has its advantages. Because the AppleScript runtime, (the component that executes all AppleScript code), handles things like memory management for you, you don't have to write code to deal with de-allocating memory you allocated earlier. However, there's a price to be paid for this convenience, and that's usually in size, (all things being equal, an application written in an interpreted language like AppleScript tends to be larger, and need more memory than an application written in a compiled language like Objective-C), and speed, (interpreted languages tend to be slower than compiled ones). But, for code that really doesn't care about either, the difference is a wash, and is up to programmer preferences/job requirements. The thing I wanted you to initially see, is that unlike Studio, which as we'll see, has a syntax implementation that "verbose" doesn't even begin to cover, AppleScriptObjC lets you get work done without having to bang out gobs of code for even simple things. The next thing we want to look at is the way that AppleScriptObjC and Objective-C have similar structure, even though the languages themselves are quite different. For example, let's look at setting up the variables for drawing the dot. To draw a solid colored circle, you need three basic bits of information: - You need the center of the circle, so you know where to start
- You need the radius of the circle, so you know how big the circle will be
- You need the color of the circle, so you know what color to use to draw and fill in the circle.
In the Objective-C version, this is done in the header, (.h) file with this code: @interface DotView : NSView { NSPoint center; NSColor *color; CGFloat radius; }The code is using features of the NSView class to create three things: - An NSPoint variable, center
- An NS color variable, *color
- A CGFloat variable, radius
Those will be used in the DotView main code to tell the application what the center, color, and radius of the circle should be so it can be drawn correctly. Now, the AppleScriptObjC version: property parent : class "NSView" property |center| : {x:0.0, y:0.0} property radius : 0.0 property |color| : missing valueThe syntax is different, but still similar. We tell the script to use the features of NSView to create the same three variables: center, radius, and color. With AppleScriptObjC, we also have to set the initial values for the variables, whereas we didn't in Objective-C, but that's a syntax difference. The reason that AppleScriptObjC's center and radius variable names have vertical bars around them is because those particular words are normally reserved by AppleScript. The vertical bars tell the AppleScript runtime "Hey, for this application, use these variables the way I'm defining them here, not the way you normally would use them." In talking with some of the AppleScript team at Apple, they told me that the bars themselves are harmless. If you accidentally put them in where they aren't needed, no harm no foul, it shouldn't affect anything adversely. The reason that color is defined to be a missing variable is because its value will be set by a UI control, and so this is how AppleScriptObjC lets you reserve variables that you're going to "hook up" to the UI in your application. The important point is, if you were to have never coded in anything but Objective-C in your life, and suddenly had to look at the AppleScriptObjC version of some Objective-C code, you'd have a far easier time of correctly interpreting what the code was doing than you'd ever have with Studio. Here, let's look at one more example. This time, we'll look at the code that creates the initial dot on application launch. First, the Objective-C code: - (id)initWithFrame:(NSRect)frame { [super initWithFrame:frame]; center.x = 50.0; center.y = 50.0; radius = 10.0; color = [[NSColor redColor] retain]; return self; }
Now the AppleScriptObjC code: on initWithFrame_(frame) continue initWithFrame_(frame) set my |center|'s x to 50.0 set my |center|'s y to 50.0 set my radius to 10.0 set my |color| to NSColor's redColor() return me end initWithFrame_The AppleScriptObjC code's a bit more verbose, but still, the similarities are undeniable. Thanks to the work the AppleScript team did to make AppleScript syntax work the way 'normal' Cocoa application syntax works, it is much, much easier to move between Objective-C and AppleScriptObjC's 'flavor' of AppleScript than it ever was to move between Objective-C and Studio's 'flavor' of AppleScript. The advantages to this aren't just in more efficient code, or fewer lines. One big advantage to this similarity is that an AppleScriptObjC programmer can read the 'normal' Cocoa documentation far easier than a Studio programmer can, because the way you use the language now follows the 'correct' Cocoa methods more closely. So rather than having to recreate the entire Cocoa documentation set for AppleScriptObjC, the AppleScript team can create a smaller set of core AppleScriptObjC documentation to help you get started, and then you can use the normal Cocoa docs for everything else. That's a huge advantage. But what about AppleScriptObjC and Studio? How does AppleScriptObjC compare to Studio? Quite favorably as it turns out. By 'quite favorably' I mean "leaves Studio in the dust, choking and wondering why those durn kids knocked its walker over". First, with Studio, you only had access to the parts of Cocoa that Studio explicitly knew about. If Apple introduced a new framework, you couldn't just use that in Studio, you had to wait for the Studio team to implement it. With AppleScriptObjC, there's none of that. AppleScriptObjC is able to use new frameworks and features as soon as they show up in the OS. Now, you could work around that limitation in Studio via the infamous "call method" which let you shell out to 'real' Cocoa code in the Studio application. It turns out, you did that a lot in a Studio application, to where a lot of people just gave in and learned Objective-C. Task ListSo from a feature standpoint, it's not even close. AppleScriptObjC wins easily over Studio. What about a code comparison? Unfortunately, I couldn't find a Studio version of DotView, but I did find another simple application that has both Studio and AppleScriptObjC versions: Task List. Task List is just what it sounds like: a simple application that lets you create and manage a list of tasks/to-dos Task ListA nice simple application, so the comparison, as with DotView, will be simple. So, first, the AppleScriptObjC version: script Task_ListAppDelegate property tableData : {} property removedTableData : {} property tableDataController : missing value property CalCalendarStore : class "CalCalendarStore" property NSMutableArray : class "NSMutableArray" property CalTaskClass : class "CalTask" on awakeFromNib() set removedTableData to NSMutableArray's array() set tableData to NSMutableArray's array() syncTaskList() end awakeFromNib on applicationWillTerminate_(application) syncTaskList() end applicationWillTerminate_ on applicationWillResignActive_(application) syncTaskList() end applicationWillResignActive_ on applicationWillBecomeActive_(application) syncTaskList() end applicationWillBecomeActive_ on addTask_(sender) tableDataController's addObject_({priority:"3", task:"", status:"Not Started", calTask:missing value}) end addTask_ on removeTask_(sender) set deleted_objects to tableDataController's selectedObjects set deleted_object to item 1 of deleted_objects removedTableData's addObject_(deleted_object) tableDataController's remove_(missing value) end removeTask_ on syncTaskList() set calendarStore to CalCalendarStore's defaultCalendarStore set theCalendars to calendarStore's calendars set todoPredicate to CalCalendarStore's taskPredicateWithCalendars_(theCalendars) set tasksInCalStore to CalCalendarStore's defaultCalendarStore's tasksWithPredicate_(todoPredicate) set tasksInTable to tableData's valueForKey_("calTask") set tasksToDelete to removedTableData's valueForKey_("calTask") set tasksAdded to NSMutableArray's array() -- Get tasks from iCal that aren't in the table, and delete tasks from iCal that we've been asked to kill repeat with aTask in tasksInCalStore if not (tasksInTable's containsObject_(aTask) as boolean) and not (tasksToDelete's containsObject_(aTask) as boolean) then -- Add a new task if it's not in the list of showing or deleted taks set priority to aTask's priority as string set |title| to aTask's |title| as string tableDataController's addObject_({priority:priority, task:|title|, |status|:"Not started", calTask:aTask}) tell tasksAdded to addObject_(aTask) else if tasksToDelete's containsObject_(aTask) as boolean then -- Delete task we were asked to kill set returnValue to calendarStore's removeTask_error_(aTask, reference) if not item 1 of returnValue then set err to item 2 of returnValue error (err's localizedDescription()) end if end if end repeat -- Update and create new calTasks based on table data repeat with tableDataItem in tableData set calTable to tableDataItem as record try set aCalTask to calTable's calTask on error set aCalTask to missing value end try if aCalTask is not equal to missing value and not (tasksInCalStore's containsObject_(aCalTask) as boolean) and not tasksAdded's containsObject_(aCalTask) as boolean then -- Delete this task, which was deleted in iCal tell tableDataController to removeObject_(tableDataItem) exit repeat else if aCalTask is equal to missing value then -- Create new CalTask for a newly created table row set aCalTask to CalTaskClass's task() set aCalTask's calendar to (get first item of calendarStore's calendars) set the calTask of tableDataItem to aCalTask end if -- Save out both the existing tasks and freshly created tasks if aCalTask is not equal to missing value then set the |title| of aCalTask to tableDataItem's task set the priority of aCalTask to (tableDataItem's priority's integerValue) set returnValue to calendarStore's saveTask_error_(aCalTask, missing value) end if end repeat end syncTaskList end scriptThat's not bad, about 73 lines of code, if you remove non-code lines. What about the Studio version? I'm not pasting it in here, because it's about 185 lines of code or so to do the same thing. Well, less, as we'll see in a bit. So let's look at some common code here, the "awake from nib" function, which is analogous to the application launch. The AppleScriptObjC version: on awakeFromNib() set removedTableData to NSMutableArray's array() set tableData to NSMutableArray's array() syncTaskList() end awakeFromNibThat's pretty simple. Set a couple variables to arrays, and run something called syncTaskList. The Studio version: on awake from nib theObject if name of theObject is "tasks" then -- Create the data source for our "tasks" table view set theDataSource to make new data source at end of data sources with properties {name:"tasks"} -- Create the data columns, "priority", "task" and "status". We also set the sort properties of each of the data columns, including the sort order, the type of data in each column and what type of sensitivity to use. make new data column at end of data columns of theDataSource with properties {name:"priority", sort order:ascending, sort type:numerical, sort case sensitivity:case sensitive} make new data column at end of data columns of theDataSource with properties {name:"task", sort order:ascending, sort type:alphabetical, sort case sensitivity:case sensitive} make new data column at end of data columns of theDataSource with properties {name:"status", sort order:ascending, sort type:alphabetical, sort case sensitivity:case sensitive} -- Set the data source as sorted set sorted of theDataSource to true -- Set the "priority" data column as the sort column set sort column of theDataSource to data column "priority" of theDataSource -- Finally, assign the data source of the table view to our data source set data source of theObject to theDataSource end if end awake from nibOof. Even if I were to take the comments out, we can still see that the Studio version is a lot bigger, and seems to require you to do a lot more work for the initial application setup. That's because it does require more work for initial application setup. Even though you use Interface Builder to build the UI elements for both AppleScriptObjC and Studio, AppleScriptObjC is able to use things the way a 'real' Cocoa application does. So unlike Studio which makes you create manually create the AppleScript implementation of the data columns that will be used by the UI, and set all the properties of those data columns, AppleScriptObjC is able to use the information that you already put into Interface Builder, and that Interface Builder provides for free. The idea behind this is trick called "bindings". I'm not going to even try to explain bindings here, but they're what allow AppleScriptObjC to not have to need all the code that Studio needs. It's not that the UI in Studio doesn't have all the bindings info available to it, it's that bindings are invisible to Studio, and so with Studio, you're stuck using the older Data Source API. So, in a sense, even though you've built the UI in Interface Builder, you have to do a lot of redundant work in Studio to manually define what the UI elements in Task List are doing, work you don't have to do in AppleScriptObjC. That's kind of the idea with Interface Builder, by the way. You create the UI elements, set them up, and then the code is able to just use them. While Studio's use of AppleScript is more 'traditional', it's also far kludgier. This is repeated over and over throughout the code. Stuff that AppleScriptObjC can do in a few lines, Studio takes a book. Stuff that AppleScriptObjC doesn't even need code for, Studio needs lines and lines and lines. Oh, and the syntax is so far removed from 'normal' Cocoa syntax, even allowing for specific language differences that there's almost no way to apply standard Cocoa documentation and sample code to Studio. The gulf between them is just too wide. However, I do feel that I should come clean about something. When I said that AppleScriptObjC took about 73 lines of code to do the same thing that Studio needs almost 200 lines of code to do, I was lying. Blatantly lying. It really only needed 24 lines of code to do what Studio did. The other 49 or so lines of code let you integrate Task List into the iCal store, so you can see your iCal tasks in Task List, and have the tasks you add to Task List show up in iCal. I don't even want to think about what it would take to do that with Studio, assuming you even could without having to use "call method" and shell out to 'real' Cocoa code. However, AppleScriptObjC does lose to Studio in one important way: AppleScriptObjC and AppleScriptObjC applications will only run on Mac OS X 10.6 or later. If you want to create applications using AppleScript for Mac OS X 10.5 or earlier, you cannot use AppleScriptObjC. AppleScriptObjC is a huge change, and it does bring with it a lot of new things you'll need to learn, and unlearn, especially if you are using Studio. But, it is also a huge leap forward in capability and features for AppleScript. It also shows, better than any amount of platitudes could, that AppleScript is not going away anytime soon. I just cannot see Apple doing this much work, and creating such a monstrously huge improvement to AppleScript just to knife it.
|
 |
 |
 |
 |
|
 |
 |

 |
| |
 |
 |
 |
 |
|
 |
 |
(Just for those who care: no, i'm not an experienced Cocoa or ObjC programmer. It should have been obvious if you knew enough to ask, but just to clear it up.) This post, we're going to take the initial save file setup we had from last week, and expand that to actually writing data to a new file. Doing this is going to require splitting up the overall writing code across three handlers in my application. In this case, we have made some minor changes to the code that executes after the user clicks "Okay" in the save panel dialog: if theSavePanelResult is 1 then
--if they clicked the 'save' button, then we want to get the path and set some flags set theDataFileURL to theSavePanel's |URL|() --get the encoded URL, which is not the file path, but we'll need it set theDataFilePath to theSavePanel's filename() --get the posix path to the file set createDataFile to true --we're creating a new data file, so this has to be true set appendToExisting to false --we are not appending data, so set to false set theFileManager to my NSFileManager's defaultManager() --create a file manager object so that we can create a blank file set theCreateFileResults to theFileManager's createFileAtPath_contents_attributes_(theDataFilePath, missing value, missing value) --creates a blank file at the path specified. Do NOT use "my theFileManager's..." because errors will happen set theFileHandle to my NSFileHandle's fileHandleForWritingToURL_error_(theDataFileURL, missing value) --use this to write to the file URL end ifSo as you look at it, you can see that all we did was make the file handle setup the last line. Note that I've set up theFileHandle as a property, because it's the lazy way to avoid scope issues. So we clicked our button to create a new file, we gave it a name, and a location, and we have a file path to get data into it. How do we build the data? Well, first, we have to define what that data is. In this application's case, it's series of lines of tab-delimited text. Each item is a bit of info about the current WiFi network, along with a timestamp for when the data was read. This can be entered as part of the track functionality, writing one line of data per second, or when you hit the refresh button, to write the data manually. The file handle is closed when you either turn off the track function, or deselect the save to file checkbox in the application UI. So let's look at building the data string. Since I have a nice handler to grab the wifi data and populate the UI with the manual refresh or with the track button, we'll add some code to that. Here's the entire loadData handler: on loadData(theCurrentInterface) currentSSID's setStringValue_(theCurrentInterface's ssid()) --set the contents of SSID field to the current SSID set theCurrentAuthMode to (theCurrentInterface's securityMode() as text) --it's an NSNumber, will deal with it later. Forcing to text works for now if theCurrentAuthMode is "0" then --mode number to mode name authMode's setStringValue_("Open") else if theCurrentAuthMode is "1" then authMode's setStringValue_("WEP") else if theCurrentAuthMode is "2" then authMode's setStringValue_("WPA1 Personal") else if theCurrentAuthMode is "3" then authMode's setStringValue_("WPA2 Personal") else if theCurrentAuthMode is "4" then authMode's setStringValue_("WPA1 Enterprise") else if theCurrentAuthMode is "5" then authMode's setStringValue_("WPA2 Enterprise") else if theCurrentAuthMode is "6" then authMode's setStringValue_("WiFi Protected Setup") else if theCurrentAuthMode is "7" then authMode's setStringValue_("Dynamic Wep 802.1X") else authMode's setStringValue_("Unknown/Invalid") end if currentChannel's setStringValue_(theCurrentInterface's channel()) --set the channel currentDataRate's setStringValue_(theCurrentInterface's txRate()) --set the data rate in Mbps signalStrength's setStringValue_(theCurrentInterface's rssi()) --set the signal strength in dbm signalNoise's setStringValue_(theCurrentInterface's noise()) --set the signal noise in dbm currentWAPMAC's setStringValue_(theCurrentInterface's bssid()) --set the MAC of the base station theTime's setStringValue_((time string of (current date))) --let's add the code to save to a new file if (createDataFile is true) and (theSaveFileFlag is true) then set theTempString to (theTime's stringValue() as text) & " " & (currentSSID's stringValue() as text) & " " & (authMode's stringValue() as text) & " " & (currentChannel's stringValue() as text) & " " & (currentDataRate's stringValue() as text) & " " & (signalStrength's stringValue() as text) & " " & (signalNoise's stringValue() as text) & " " & (currentWAPMAC's stringValue() as text) & " "
set theFileString to my NSString's stringWithString_(theTempString) set theFileData to theFileString's dataUsingEncoding_(NSUTF8StringEncoding of current application) theFileHandle's writeData_(theFileData) --you could probably also use fileHandleForWritingToPath here, but since URLs are the way of the future --we should use them where we can --we stash the close function where it's going to be actually used end if end loadDataThere's really not much going on here. This handler, (and you can tell it's a 'normal' AppleScript handler because it doesn't have an underscore in the name), is passed a WiFi interface object, theCurrentInterface. It then pulls data from that to set various text fields in the Application UI. For example, we grab the SSID of the current WiFi network via theCurrentInterface's ssid(), and use that to set the contents of that text field in the UI via currentSSID's setStringValue_(theCurrentInterface's ssid())Since the enumeration for the authentication mode can be one of 9 values, including "other", we have a series of if then else statements to handle that. To get the time string we use for theTime, instead of using the Cocoa technique, and NSDate/NSDateComponents, we use a more traditional AppleScript way: time String of (current date). It works just as well as any other method for our needs, and is simpler to code. adding that to the UI is then a single line: theTime's setStringValue_((time string of (current date)))So now, we have all the information we need to build our string that we want to write to file. To do that, we again, combine Cocoa and traditional AppleScript to build a tab-delimited line with a trailing return: set theTempString to (theTime's stringValue() as text) & " " & (currentSSID's stringValue() as text) & " " & (authMode's stringValue() as text) & " " & (currentChannel's stringValue() as text) & " " & (currentDataRate's stringValue() as text) & " " & (signalStrength's stringValue() as text) & " " & (signalNoise's stringValue() as text) & " " & (currentWAPMAC's stringValue() as text) & " "The blank spaces in between the quotes are the presentation of the tab formatter, \t. The odd quotation mark by itself is the presentation of the return formatter, \r. So now we spend three statements on converting theTempString to an NSData object and writing that to a file. First, we create the NSString object: set theFileString to my NSString's stringWithString_(theTempString). Pretty clear, we use the stringWithString function, passing it the temp string. Next, we encode theFileString and convert it to an NSData Object: set theFileData to theFileString's dataUsingEncoding_(NSUTF8StringEncoding of current application)We're using UTF8 encoding, because that's the better way to do things, but there are a variety of encodings available, including MacRoman, etc. One thing to watch here is that you have to set the encoding as "<encoding method> of current application", or it fails miserably. Finally, we write that NSData object to the file we created via the file handler: theFileHandle's writeData_(theFileData)Every time this handler is called, as long as createFileData and theSaveFileFlag are both true, we get a line of data written. So what happens when we're done writing? Well, it wouldn't make a lot of sense to put that code here, so we put it in the functions that we use when we're done tracking data, or when we deselect the save file checkbox. First, when we stop tracking: on timerFired_(thetimer) --this handler runs the actual code for the timer if trackButtonSTate is 1 then --'on' loadData(theCurrentInterface) of me --grab stats once per second else if trackButtonSTate is 0 then --"off" thetimer's invalidate() --kill the timer theFileHandle's closeFile() --close the file handle we've been writing to set theSaveFileFlag to false --kill the save file flag set createDataFile to false --kill the new file flag set appendToExisting to false --kill the append file flag saveToFileCheckBox's setIntValue_(0) --disable the checkbox my createNewDataButton's setEnabled_(false) --disable the button to create a new data file my appendToExistingDataButton's setEnabled_(false) --disable the button to append to an existing data file end if end timerFired_We put the file handle cleanup code in the same block as the timer cleanup code. When you disable tracking, it's going to run the code to kill the timer that's controlling how fast the tracking is happening anyway, so it makes sense to put it here. It's only one line to shut down the file handle: theFileHandle's closeFile()That's it, the file handle is closed, file writing is done. The rest is just cleaning up properties and resetting controls to give a better indication to the user that they're no longer recording data to a file. The same basic thing happens if they disable the save to file checkbox: on saveToFile_(sender) --when you click the "Save to file" checkbox this ONLY controls the button states, not what the buttons do if sender's intValue() is 1 then --if you're checking the checkbox set theSaveFileFlag to true my createNewDataButton's setEnabled_(true) --enable the button to create a new data file --the "my" is critical to having the now enabled button be able to send events --without "my", the button knows it's enabled, nothing else does my appendToExistingDataButton's setEnabled_(true) --enable the button to append to an existing data file else if sender's intValue() is 0 then --if you're de-checking the checkbox theFileHandle's closeFile() --close the file handle we've been writing to set theSaveFileFlag to false --kill this file flag set createDataFile to false --kill the new file flag set appendToExisting to false --kill the append file flag my createNewDataButton's setEnabled_(false) --disable the button to create a new data file my appendToExistingDataButton's setEnabled_(false) --disable the button to append to an existing data file end if end saveToFile_Nothing new here, close the file handle, reset things, and bob's your uncle. Once you wrap your head around things, especially the encoding method thing, writing data to a file is pretty easy. Really, that was the biggest frustration for me, because even reading the Cocoa docs on this, there was nothing to really indicate that had to happen. Again, many thanks to Shane, Craig, and everyone else on the AppleScriptObjC list for all their help.
|
 |
 |
 |
 |
|
 |
 |

 |
| |
 |
 |
 |
 |
|
 |
 |
Before we get into today's bit: - File URLs are not File Paths
- Just because NSFileHandle can deal with URLs, that doesn't mean NSFileManager does. So you can use either a path or a URL to create a handle to an existing file, but to create the file itself, you have to use the path
- Documentation that interchanges terms like "path" and "url" makes me want to go all stabbity-stabbity
- Knowing when NOT to use "my" is important too
Now, on to today's bit. I've been spending most of my time of late dealing with the "Cocoa" way to save and open files. Now, there's no functional reason to do this, AppleScript has had some solid ways to do this for some time now in Standard Additions. So, if you want to create a new file, open a write handle to it, write some data to it, then close the file handle, you have: set theFile to choose file name with prompt "enter a new file name to be created, or choose a new file that will be obliterated" default name "newfile.txt" set theFileHandle to open for access theFile write permission true write aBigBunchOfData to theFileHandle close access theFileHandleIt's pretty straightforward. Choose File name lets you 'choose' a file that may not exist yet. We then use Open For Access with write permission to get a handle to the file that we can use for writing data. Write then shoves data into the file handle, and close access closes off the file handle. If you pick an existing file, any data in that file is obliterated. (We'll deal with appending data later.) Doing this in AppleScriptObjC, at least the "Cocoa" way is a bit more involved, but worth digging into, as a teaching exercise alone. So, here's the code block I have for creating a new file. Note that I'm not yet actually writing data into it, just getting everything ready to do so: on createNewFile_(sender) --create a new file set theSavePanel to my NSSavePanel's savePanel() --create the save panel object theSavePanel's setMessage_("Create New Data File") theSavePanel's setAllowedFileTypes_(theFileTypeArray) --we want to be specific here, and only allow certain types, in our case, text theSavePanel's setExtensionHidden_(0) --in the AppleScriptObjC is not objectiveC file: for bools, use 1 or 0 not YES or NO --if you don't, you'll get fussed at and you'll never know why theSavePanel's setAllowsOtherFileTypes_(1) --yes we only want text, but we don't want to be dicks about it. --if someone really wants something else, sure. --if someone really wants to use .dat or whatever, fine, they can theSavePanel's |setNameFieldStringValue_|("WiFi Analyzer Data.txt") --i named this as STringValue once and AppleScript being, well --AppleScript, it will hang on to that forever! so the pipes around it help me deal with that set theSavePanelResult to theSavePanel's runModal() --display the panel and get the binary result if theSavePanelResult is 1 then --if they clicked the 'save' button, then we want to get the path and set some flags set theDataFileURL to theSavePanel's |URL|() --get the encoded URL, which is not the file path, but we'll need it set theDataFilePath to theSavePanel's filename() set createDataFile to true --we're creating a new data file, so this has to be true set appendToExisting to false --we are not appending data, so set to false set theFileManager to my NSFileManager's defaultManager() --create a file manager object so that we can create a blank file set theCreateFileResults to theFileManager's createFileAtPath_contents_attributes_(theDataFilePath, missing value, missing value) --creates a blank file at the path specified. --Do NOT use "my theFileManager's..." because errors will happen set theFileHandle to my NSFileHandle's fileHandleForWritingToURL_error_(theDataFileURL, missing value) --use this to write to the file URL --you could probably also use fileHandleForWritingToPath here, --but since URLs are the way of the future --we should use them where we can log theFileHandle theFileHandle's closeFile() --we'll use this later end if end createNewFile_I left the comments in, as they do a decent job of explaining each statement. One thing I learned is that Cocoa is big on "create the object, THEN do stuff to it" whereas traditional AppleScript lets you avoid the create the object. For example, you don't have to create a file manager object, so that you can use that to create a new blank file, which then lets you get the file handle to it so that you can write data to it. With AppleScript, you just get the file path for the new file, get the handle, write, and go. This also follows with the save panel. You create the save panel, configure the save panel, then actually run the save panel so it displays. (Also, if anyone has some sample ASOC code for beginSheetModalForWindow:completionHandler: or beginWithCompletionHandler: and wanted to post it in the comments or link to where it is, I'd not cry.) Next in line...actually writing data, and then appending data to the end of an existing file. Woohoo!
|
 |
 |
 |
 |
|
 |
 |

 |
| |
 |
 |
 |
 |
|
 |
 |
Okay, so i've been adventuring with AppleScriptObjC, because a) FaceSpan 5 is in limbo, and will be for some time, (insert wailing noises here) and b) AppleScriptObjC is what i've been raging at Apple to give me for some time now, and it's not 100%, but it's pretty close. Any posts i put up for AppleScriptObjC will have "AppleScriptObjC" as the category, so you can find them easier. My first application is really a port of a FS 5 application I wrote that's a WiFi signal analyzer. eventually, it will show you stats, let you automatically track those stats over time, refreshing once per second, save that data to a new file, or append to an existing file, and show you a live graph of signal vs. noise in the app window. I had all this working in FS 5, so i aim to get it all working in ASAppleScriptObjC. First, a huge, huge, huge thank you to both Shane Stanley and Craig Williams. The both of them have been a huge help to me in this, and the community is far better for having them in it. Second, if you haven't yet gone, run to MacScripter's AppleScriptObjC forums, it's a hell of a resource, and Craig has a great set of tutorials that were, and are a monstrous help to me. In one sense, my refusal to deal with the unending limitations of ASS have been a help, as I don't have any bad habits to break from that direction. Since I don't know Objective C, I don't have to deal with those differences either. However, the lack of ObjC knowledge is a bit of a pain in the keister when reading Apple developer docs, although not as much as I thought it might be, thanks to Craig's tutorials. So, some quick shots that I learned: - Don't rely on Xcode or anything else to tell you when you're missing a framework. You can reference CoreWLAN all day long, and you won't know you forgot to add the framework until you try to use some of the methods or properties. boo.yah
- The Xcode debugger still doesn't work for beans with AppleScript, and even what little it worked for ASS, was too complicated to set up. There's two issues here. First, the way I want it to work is I set a breakpoint, and when it hits that breakpoint, it stops and i can step from there. I don't know why this doesn't work this way, don't care, because while i can dig up the email that tells me how to do it, seriously, this shouldn't be that hard. Enable breakpoint, stop on breakpoint.
The other issue of course is that Xcode's debugger is GDB, and it's simply not designed to work, nor shall it ever work well with a higher-level language with dynamic syntax like AppleScript. I really wish Apple would throw a gob of money at Mark Aldritt so that he could write the AppleScriptObjC debugger implementation for Apple. He's the only one to ever really get it right.
- Join the AppleScriptObjC list. Less noise than the ASS list, so it's quite useful if you don't really care about ASS, which I don't.
- Before I use a class, I make sure to create the property for it, so lots of property NSTimer : class "NSTimer" kinds of things. It's pretty handy as a habit.
- Setting up and using stuff in Interface Builder really is as easy as the tutorials make it seem.
- Learning how to translate ":" to "_" is a pain in the butt, but important. For example, in Objective C, you have:
(void)setNameFieldStringValue:(NSString *)value
The AppleScriptObjC version is:
theSavePanel's setNameFieldStringValue_("File.txt")
That's pretty easy. Where it gets tricky is when you have stuff like this example from NSTimer:
(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
The AppleScriptObjC version:
NSTimer's scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(1, me, "timerFired:", "", true)
Took me a few readthroughs to figure out that with AppleScriptObjC, that first parameter was a bit odd, and that i should see it as being, (assuming the method existed with only an interval parameter):
NSTimer's scheduledTimerWithTimeInterval_(1)
Once I grokked that there's always an underscore for every colon, even if it's just a trailing colon, things got a lot easier.
- Another thing about NSTimer that I probably would have never realized on my own, is that to use NSTimer in this way, that selector bit should be read as "create another handler for theTimer that has the same name as the selector, and that this is where all the code for the timer will run:
on timerFired_(thetimer)
It's also a little odd, to me at least, to have the code to kill the timer in the same handler as the timer's functional code, but okay, sure. (there's still a bit of "take it on faith" with regard to NSTimer for me at this stage.)
- "my" is very important. Say for example, you have a button (or two) that are disabled by default, and you want them to become enabled when another checkbox is clicked. Well, you set them as disabled in Interface Builder, so your initial state is set. Then you want them to be enabled when that checkbox is enabled. You might want to do this:
on saveToFile_(sender) if sender's intValue() is 1 then createNewDataButton's setEnabled_(true) appendToExistingDataButton's setEnabled_(true) else if sender's intValue() is 0 then --if you're de-checking the checkbox createNewDataButton's setEnabled_(false) appendToExistingDataButton's setEnabled_(false) end if end saveToFile_
and that will work, sort of. The buttons will enable and disable, but even after you wire them up to code, they won't DO anything when you click them. They know they're enabled, the application doesn't. For that bit to work, you need "my":
on saveToFile_(sender) if sender's intValue() is 1 then my createNewDataButton's setEnabled_(true) my appendToExistingDataButton's setEnabled_(true) else if sender's intValue() is 0 then --if you're de-checking the checkbox my createNewDataButton's setEnabled_(false) my appendToExistingDataButton's setEnabled_(false) end if end saveToFile_
Now, when you click the newly enabled buttons, stuff will happen. Stuff happening is good.
- Objective C may tell you to use YES and NO for BOOLs. AppleScriptObjC wants 1 and 0, and don't you forget it.
- Just because AppleScript is case-insensitive, Objective C is not, and will mess with your head until you realize that.
- I really wish Apple's IT documentation was as good as its developer docs.
That's enough for now, I'll start going through some of the actual code stuff I'm working on later.
|
 |
 |
 |
 |
|
 |
 |

 |
| |
 |
 |
 |
 |
|
 |
 |
shit, not even cool shit like PZ Myers. I get lame crazy shit. so here's what started this, on the Apple client management list...a terribly excited, rather confused woman is convinced she's being hacked. it goes back and forth with some folks, who mean well, but are just amping shit up until she sends this: Here's where things stand. I erased and installed the OS from a system disk in the DVD drive. Logged back in. Still heard motor racing so checked with lsof command in Terminal. Saw lyld processes, PIPE processes, mDNS (on Leopard) and more alarming things. Checked System Profiler found awake on net set to yes, found Bluetooth on, and other discrepancies. Shut down.
C boot again. From Disk Utility checked file image. In /private/var/ found launchd folder filled with a sock terminal, found folder named mds holding install.lock, lick, mdsDirectory, and object. Found alias folder named run with appfwd.pid, asl_input, com.apple.blued.launchd, config.pid, diskarbitrationd.pid, installer, mDNSResponder, a ppp.confd terminal, syslog, syslog.pid, and utm.px.
Other directory level folders held only a file named com.apple.stackshot.plist.
All these files have been suspects in this investigation, so I figure a kernel level rootkit is usurping the system.
I partitioned, 7-0's erased, and still heard the hard drive racing everytime I began a new operation. Very hard on the drive. Because if the racing drive when no operations required it, I did a lsof again. Dynamic load libraries running amuck. PIPE. The whole host of dementors were still there and still running.
My disk name changed to rdisk1s03. I think the "r" means image file.
I checked the Installer Log from th C drive boot. Found a lot of evidence that includes these unlikely terms: localhost LCA [65]. (this is the villan). Folder Manager create a folder; CPSGetProcessInfo; OSInstaller [144] Folder Manager create a folder; at this point I get created as localhost Unknown[66]
Lots more bad stuff and then ENV: DYLD_NO_FIX_PREBINDING=1
ENV: SHLVL=1
ENV: OS_ INSTALL=1
ENV:_=/System/Installation/CDIS/LCA.app/Contents/MacOS/LCA. (you may remember LCA was the first user, so I think we have a point of origin.)
There's much more, but I need a rest. Typing all this from a pad to my iPhone is more labor intensive than my usual style.
I cannot erase the drive. I have to get in at the preBoot level or something. Any ideas?
Also I think we may want reinforcements. Any other expert sites worthy in your eyes. I'm not looking forward to typing on this cute little phone but if I can't save my two compromised notebooks I'm gonna be doing it for a long time.
Thanks everyone for this afternoon's work. Nap time.
Letha
Letha Deck
On Jan 4, 2010, at 2:50 PM, Steven Kolins <smkolins@email.unc.edu> wrote:
> > On Jan 4, 2010, at Monday 2:23 PM, Letha Deck wrote: > >> none of the techniques worked, yet, because they've invaded too >> deeply into my Mac territories. I cannot change any files or folders. > > > If you have a working Mac somewhere you may be able to gain access > by putting the machine in target mode. You will need a full firewire > cord (newer is Firewire 800, so called "9 pin to 9 pin".) > > Hold the T key down at boot and the machine will slave out the hd > out the firewire port to any Mac waiting that is connected by a > firewire cord. Then you get a r/w version of the hd. Should be safe > if you don't run executables from the drive - just aim things > scanner programs at the drive. > > = - - - - - - - = > Steven Kolins
Okay, so she's off in the weeds. DEEP into the weeds now. I reply: Okay, hold on, let's slow down and rather than playing "Ready, Fire, Aim", let's actually look at what's going on, so that we separate "I'm not sure what this does" from "OMGHAXX0RZ!!!111"
On 1/4/10 6:34 PM, "Letha Deck" <lethadeck@mac.com> wrote: > Here's where things stand. I erased and installed the OS from a system > disk in the DVD drive. Logged back in. Still heard motor racing so > checked with lsof command in Terminal.
So first thing: "motor racing" has no meaning at all. None. You have to not only be specific, but use terms for whose meaning we can all agree upon.
What do you specifically mean by "motor" in this context. Fan? Optical drive? Hemi?
> Saw lyld processes, PIPE > processes, mDNS (on Leopard) and more alarming things.
On a brand new OS install, all of those things are absolutely normal. Completely.
Even on a fully patched system running 10.5.8, I have numerous processes that are using dyld. Since dyld is used with various libraries in the OS, it would be far more frightening to NOT have multiple copies of dyld running.
Again, the same with PIPE. In fact, to check for just PIPE processes with lsof, you have to actually use PIPE.
mDNSResponder and associated processes using mDNS in Leopard, again, perfectly normal by default.
*None of those are alarming*
> Checked System > Profiler found awake on net set to yes, found Bluetooth on, and other > discrepancies. Shut down.
Both of those are the correct default values for a new install from the leopard DVD.
> > C boot again. From Disk Utility checked file image. In /private/var/ > found launchd folder filled with a sock terminal,
Normal for sockets, a rather common Unix tool
> found folder named > mds holding install.lock, lick, mdsDirectory, and object.
Yep, spotlight processes are normal
> Found alias > folder named run with appfwd.pid, asl_input, com.apple.blued.launchd, > config.pid, diskarbitrationd.pid, installer, mDNSResponder, a > ppp.confd terminal, syslog, syslog.pid, and utm.px.
All of those are normal, and rather critical.
> > Other directory level folders held only a file named > com.apple.stackshot.plist.
Yes, that's the facility that the OS uses to capture stack traces for various automatic reasons. 'man stackshot' explains it fully.
> > > All these files have been suspects in this investigation, so I figure > a kernel level rootkit is usurping the system.
Forgive my bluntness, but those are no more indicative of a rootkit than they are of a yeti.
> > I partitioned, 7-0's erased, and still heard the hard drive racing > everytime I began a new operation. Very hard on the drive. Because > if the racing drive when no operations required it, I did a lsof > again. Dynamic load libraries running amuck. PIPE. The whole host of > dementors were still there and still running.
As they are rather needed for the OS to operate, that you keep finding them is absolutely unsurprising.
> > My disk name changed to rdisk1s03. I think the "r" means image file.
Not really. Google /dev/rdisk.
> > I checked the Installer Log from th C drive boot. Found a lot of > evidence that includes these unlikely terms: localhost LCA [65]. > (this is the villan). Folder Manager create a folder; > CPSGetProcessInfo; OSInstaller [144] Folder Manager create a folder; > at this point I get created as localhost Unknown[66] > > > Lots more bad stuff and then > ENV: DYLD_NO_FIX_PREBINDING=1 > > ENV: SHLVL=1 > > ENV: OS_ INSTALL=1 > > ENV:_=/System/Installation/CDIS/LCA.app/Contents/MacOS/LCA. (you may > remember LCA was the first user, so I think we have a point of origin.) > > There's much more, but I need a rest. Typing all this from a pad to my > iPhone is more labor intensive than my usual style.
None of this is abnormal nor evil.
> > I cannot erase the drive. I have to get in at the preBoot level or > something. Any ideas?
Yes. First rule of sysadmining: "Just because I don't know what something is does not imply it is bad or useless. It really means I have more to learn.
> > Also I think we may want reinforcements. Any other expert sites worthy > in your eyes. I'm not looking forward to typing on this cute little > phone but if I can't save my two compromised notebooks I'm gonna be > doing it for a long time.
Based on this, you have great need of education, not panicking. Everyone starts out with that same need. Those of us who do well in this field realize that need never goes away nor diminishes.
Yes, i was in fact being deliberately not snarky. Well, not very. I then get this shit offlist: Thanks for the information. I am actually not in your field. I came to your list to learn from experts. I wrote that in my introduction. Your style is rough. I was told about a hostile post or two from others but I am on digest. Just got it. I felt embarassed and humiliated. That part I don't like. You gave me some good information. I appreciate it.
I am not a network. I am a single user at home. My router is not even plugged in for security reasons.
I would feel rather silly describing anything else to you. Thank you for your interest. Da Fuck? for taking a lot of time with lsof to doublecheck what I already knew and trying to un-panic her, I get this? oh.fuck.no. On 1/5/10 3:43 PM, "Letha Deck" <lethadeck@mac.com> wrote:
> Thanks for the information. I am actually not in your field. I came to > your list to learn from experts. I wrote that in my introduction. Your > style is rough. I was told about a hostile post or two from others but > I am on digest. Just got it. I felt embarassed and humiliated. That > part I don't like. You gave me some good information. I appreciate it.
Your feeling silly or anything else is most certainly not my fault, and your reaction to things is neither my fault, nor unique. Everyone in 'my' field, *everyone* has had a moment of freaking out about something that turned out to be absolutely normal.
We also all felt silly about it. In many cases, various nicknames synonymous with 'crash' were assigned. One place I worked had the "Crash Craddock" award for best screwup of the month. Mildly mean, but not taken personally.
Stop blaming others for this, and your feelings of embarrassment resulting from it, not our fault.
> I am not a network. I am a single user at home. My router is not even > plugged in for security reasons.
Not that you'll listen, but you're really confusing paranoia and security. Even real security experts plug in their routers. They don't work terribly well otherwise.
> I would feel rather silly describing anything else to you. Thank you > for your interest.
So let me get this straight: rather than amping up the state of panic you were in, I went through all the things you were rather freaking out about, and pointed out that these are normal things.
Line
By
Line
I pointed out that they were not in fact indicative of a rootkit, nor any other form of malicious attack.
I pointed out that like everyone on that list, including myself, the way to get past the the assumptions that were causing you to panic was to keep educating yourself and to never stop.
And amongst all that, you're angry that I didn't encourage you to panic more in a nice way? Seriously, you'd rather get bad advice that does no good whatsoever, and keeps you in a state of panic for days, rather than endure the pure evil that is light sarcasm, and information that points out your panic is unwarranted, and that you should not approach 'strange' as unknown?
So in your world, nice and wrong beats sarcastic and correct?
You go on with yo' bad self with that attitude, but helping yourself, you are not. Of course i replied. Duh. So she has to reply back, still whining: I'm sorry. I couldn't finish reading your email. I saw your website. It seems you have a consistent personal style that offends me to the point where I don't desire to read you so I stop. Some people like that Rush Limbaugh thing you've got going on. The bombastic quality of that communication style isn't compatible with an intellectual bookworm like myself.
From what I did read and scan I found that you are not a careful reader. That makes you unreliable. You are include too many personal admonishments. If I don't respect you why would I respect your lectures on comportment?
I don't want to spend another millisecond considering you or anything you have to say. It's not like you are saying much that can't be heard from a zillion other Joes. I'd rather listen to one with a handsome manner.
Again. Thanks. I'm deleting you. Do the same with me. So now i'm in the "what are you, fucking 12?" mode...and i just have to have fun with 'literary bookworm'. Pretentious bint: On 1/5/10 9:20 PM, "Letha Deck" <lethadeck@mac.com> wrote:
> I'm sorry. I couldn't finish reading your email. I saw your website. > It seems you have a consistent personal style that offends me to the > point where I don't desire to read you so I stop. Some people like > that Rush Limbaugh thing you've got going on. The bombastic quality of > that communication style isn't compatible with an intellectual > bookworm like myself.
Ah, the "you're rude, so your words have no meaning" technique. Amusing as it is useless. I shall then hazard that you've little Ambrose Bierce in your awesome literary collection, for I imagine the 'bombastic' quality of his bon mots would likely send you to the drawing room with a critical case of the vapors.
> > From what I did read and scan I found that you are not a careful > reader. That makes you unreliable. You are include too many personal > admonishments. If I don't respect you why would I respect your > lectures on comportment?
You accuse me of being a poor reader, yet you admit to not reading my email. Is 'irony' a word you're familiar with, because that bit is *rich* with it. Oh, if you're going to assume a position of literary superiority, then you might want to grammar-check your missives. "You are include too many personal admonishments" is not the kind of mistake that someone who is truly an "intellectual bookworm" would make.
> > I don't want to spend another millisecond considering you or anything > you have to say. It's not like you are saying much that can't be heard > from a zillion other Joes. I'd rather listen to one with a handsome > manner.
I'm surprised an 'intellectual bookworm' such as yourself would say that, considering the rich history of evil using fair form, with a handsome manner to deceive the innocent, even as the truth was ignored because it was neither pretty or smooth. Your aspirations of literary familiarity seem a bit shallow.
Of course, it's also too bad I'm the only one who bothered to explain to you what was going on with your computer, or that you weren't being hacked. I should have let you keep wiping out your hard drive and giggled at the waste of time from the sidelines. Instead, I took a bit of pity on you. How sharper than a serpent's tongue is the lack of gratitude.
As I thought, you don't care about the quality of content, only the quality of tone. Kind of like a puppy, in that you can threaten it with dire consequences, but as long as you do so in a cutesy obsequious tone, it thinks you're praising it to the heavens.
> > Again. Thanks. I'm deleting you. Do the same with me.
Oh emperor? The new outfit? Looks *fantastic*.
Handsome words, weren't they. Annnnd the reply. shorter now, i think she's getting tired of it: Really dude. Don't waste your time. I have discipline. Your work is on the trashbin. Save your energy v I can resist you. Not sure if that was a D&D reference or not. Whatever: > Really dude. Don't waste your time. I have discipline. Your work is on > the trashbin. Save your energy v I can resist you.
Your continuous replies give lie to that. But you keep saying that. If you say it enough, you may even believe it.
Shouldn't you be wiping your hard drive, the dyld hackers'll getcha! Yeah, i know, i'm just being a dick at this point, but what the hell, it's fun right? Then we take the abrupt left into CRAAAAAAAAZYTOWN: Gee, I hope you didn't send an apology last time because this email is to notify you that I have asked you three times to cease communicating with me, yet you continue to write me.
John, you are harassing me. You need to let go of this and get on with your life.
If you decide not to respect my wish that you stop emailing me at my personal address, I am going to report your behavior to all the appropriate officials of the mailing list.
This is my last request and your only warning.
For the record, do not call or visit or stalk me in any way because I will report you to the police and if necessary, have you prosecuted. I have to admit to having that 'poleaxed head-cocked puppy' look for a few. Rampant paranoia will do that. But, i have no sense, and less restraint, so: > Gee, I hope you didn't send an apology last time because this email is > to notify you that I have asked you three times to cease communicating > with me, yet you continue to write me.
You wrote me, privately. You did so to, literally, scold me, and tell me that I was a bad person. I replied.
Lather rinse, repeat
> > John, you are harassing me. You need to let go of this and get on with > your life.
Um, you started this with your original email to me, remember? I don't suffer inane emails from someone I mistakenly tried to help.
> > If you decide not to respect my wish that you stop emailing me at my > personal address, I am going to report your behavior to all the > appropriate officials of the mailing list.
Now you're threatening me to the EMAIL LIST? How about YOU stop starting fights and then crying when the other person doesn't run away from your vacuous inane idiocy.
But by all means, go right the hell ahead.
> > This is my last request and your only warning.
I dare you to report me to the list administrator. I double-dog dare you.
> > For the record, do not call or visit or stalk me in any way because I > will report you to the police and if necessary, have you prosecuted.
I don't know, nor do I wish to know your phone number or address. But here's one. How about YOU stop threatening me, and I won't post this entire email chain on my web site so that anyone else who reads your paranoid ramblings on a mailing list knows better than to help you. (actually, since you're threatening me legally, that might be a really good idea on my part, so that this is all out in the open.)
Pro Tip: don't harass people and then get whiny when they don't react well. You emailed me at MY 'private' email address to harass me. Did you really expect me to thank you?
I'm sure there's another part of the internet you can babble at. Run along now and do so.
Gotta give her credit, she took the dare. When do I get crazy-assed bikini models with sexual addiction issues lurking in the bushes? I think i could handle that. Instead, i get this tripe. sigh.
|
 |
 |
 |
 |
|
 |
 |

 |
| |
 |
 |
 |
 |
|
 |
 |
So me and a couple of friends, (Peter Cohen of Macworld and Darby Lines, aka The Angry Drunk) are doing a podcast. If you like angry, overly profane ranting about the unfathomable size of stupid on the Internet, then you can either go to the site and subscribe to the podcast, or if you are into teh iTunes, then you can subscribe via iTunes too. (Yes, you can actually search the iTunes Music Store for "Angry Mac Bastards", and we show up. hee.) Note: all three of us are either currently or ex-IT, and Peter has to actually go into the Macworld.com and play in the forums as part of his job.... Angry sarcasm and profanity, we has it.
|
 |
 |
 |
 |
|
 |
 |



 |
| |
 |
 |
 |
 |
|
 |
 |
Every once in a while, I think to myself, "Self, you should rejoin metaquotes, it's not a bad community after all" Then I have some reason to read the comments, and I remember why I left, and holy fuckoly, am I glad I did. Like the recent dramaaaaady, caused by benkenobigal metaquoting a really awful joke I made. Yes, I know. It's tacky. It's kinda icky. Yes, I'm well aware the woman had a C-Section, and therefore her yoni, her womanly well, her source of female power, the only good force in the universe is unharmed. So Fucking What. What I love is that the same people who are calling me sexist, et al for saying, and I quote myself: I bet that if she spreads her legs too quickly, she sucks her underwear halfway up to her ovaries. Feel free to titter away at jokes about the Duggar family's humpteen kids and various "Holy shit, it's a vagina not a clown car". In the spirit of the same kind of picayune shit the metaquotes dillholes are employing, I'll point out that Mrs. Duggar had those kids one at a time, not in groups of eight, and therefore, the clown car metaphor DOESN'T EVEN FUCKING APPLY TO HER, YOU FUCKING DINGALINGS. It actually applies more, in spite of the c-section, to Ms. Suleman, who had eight kids at once, and therefore had a LOT OF PEOPLE in a VERY SMALL SPACE...just like a fucking clown car. If you're going to rag on MY metaphor, don't misapply one yourself. Oh, and for the "some things aren't funny crowd?" <carlin>Fuck you</carlin> It's all funny. Rape, incest, you name it, there's something funny about it. 9/11? That's funny too, bitches! I'd say they all have sore pussies, but I don't think they've had another human near that area without money being exchanged in decades. O noes, I made a sloppy pussy joke. Shit, that's nothing. You want bad sexist jokes? What do you tell a woman with two black eyes? Nothing, you already done told her twice. What's the difference between a dog and a woman? The dog learns if you beat it hard enough. Why is sex with a sheep better than sex with a woman? At least the sheep is quiet after you eat it. Why is a woman's pussy and asshole so close together? So after the roofies kick in, you can carry her home like a six-pack. Why are there necrophiliacs? They finally got the bitch to shut up, why get rid of her now? What do you call that thing you wipe your dick off on after you get done jacking off? "Honey". What's the correct way to ask your woman for a blowjob? Ask? And that's just two minutes worth. They can get a lot worse. Now, while people are having their little, "SEE! HE'S REALLY A MYSOGONIST", those of you who are sane will realize that civilization didn't collapse, nor are men roaming the streets dragging women by the hair. Did your house burn down? Did you lose the right to vote? Did you have to quit your job so you can make babies? Were you gang-raped by circus midgets wearing Man Coulter masks? NOOf COURSE it was tacky and icky, I knew that when I posted it. What I love are the "more feminist than thou" crowd acting like that joke, or (most likely), the ones I just posted, are somehow great assaults on women, or an attitude that needs correcting. Get over yourselves. Shouldn't you be, you know, working to correct real problems instead of spazzing out on metaquotes? Oh wait, LJ, never mind. So to benkenobigal, unleashedfreak, veronica_rich, lillyluna and the others who get tacky, icky, tasteless humor, I'm sorry you got sucked in to that draaaaaamedy. But you're welcome here anytime, it's all good.
|
 |
 |
 |
 |
|
 |
 |




 |
| |
 |
 |
 |
 |
|
 |
 |
So a day or so ago, my wife discovers something that really pissed her off...her artwork, used without permission, in a Facebook RPG, Hammerfall. So, being the industrious sort she is, she realizes that it's not just her. There's maaaaany artists who have been ripped off by Hammerfall. So they start going through the artwork. And emailing each other. Okay, so here's the thing...nothing, and I mean NOTHING is as fierce as a bunch of artists who realize you've been stealing their work. So they start pointing it out to Hammerfall. Hammerfall starts trying to bullshit them about it. The money quote: We have addressed this, we're not stupid here, all the art we used is either under creative commons license or similar, or we contacted the artists for permission, or was bought off of stock photography sites. If an artist thinks this isn't the case, please send us a message. Oh Dennis, I beg to differ, you are stupid, and no, you in fact did not get permission for it all, nor was it creative commons/similar, nor did you contact all the artists for permission, etc. See, here's the thing...that whole "my wife discovers something that really pissed her off...." thing? And your claim that you have the right to all the artwork you're using? NoYouDon'tHavePermissionForItAllDumbassNow, maybe someone lied to you. Maybe you were mislead. But you have a community that holds a grudge for a long time, is really persnickety about their IP, contains quite a few of the artists worth paying for RPG art, and you're trying to bullshit them? Does the phrase "Burning your bridges" mean anything? So yeah, if y'all want to have some fun, let Mr. Kimbell know that artists aren't stupid, and they know who they license shit to.
|
 |
 |
 |
 |
|
 |
 |



 |
| |
 |
 |
 |
 |
|
 |
 |
And you know, you'd think it wouldn't anymore. You'd think that after the continual "fuck off' that the Acrobat team gives the Mac community, (Fuck your AppleScript, it's VBA or nothing, and lemme tell you, I think even with the return of VBA, it'll be nothing. The Acrobat team has continually lied about Office integration on the Mac for how many years now? You think I'll believe a fucking thing they say sans released product? Oh SHIT no! Sorry Lampwick, you'll have to work the salt mines solo), that it'd be easier to deal with. Nope, still sucks. "Oh, just use FlashPaper to convert things to Flash for use with Acrobat Connect...wait, Mac user? Yeah, fuck off hippie, go get a real OS". The sad thing is, Acrobat is really a decent product, but it's hampered by a team that only cares about you if you're an all-Windows company with > 30,000 seats. They could give a fuck all about you if you're a small company, and if you have Macs, you can never have enough for them to give a squirt of piss about your needs. Mac users are pretty much lucky they didn't keep the PDF spec private, and forbid its use on anything but Windows. (Yeah, I will in fact bet that there's quite a few people in that team who wish they could take that product Windows-only at every possible level.) I imagine I'll get it high and hard a few more times from the Acrobat team while trying to use Acrobat Connect. Too bad they aren't more like the CS team proper. Technorati Tags: Adobe Acrobat Team = Annoying
|
 |
 |
 |
 |
|
 |
 |

|
 |
|
 |