I'm looking for an easy way to parse VCALENDAR data with objective-c. Specifically all I am concerned with is the FREEBUSY data (See below):
BEGIN:VCALENDAR
VERSION:2.0
METHOD:REPLY
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VFREEBUSY
UID:XYZ-DONT-CARE
DTSTART:20090605T070000Z
DTEND:20090606T070000Z
ATTENDEE:/principals/__uids__/ABC1234-53D8-4079-8392-01274F97F5E1/
DTSTAMP:20090605T075430Z
FREEBUSY;FBTYPE=BUSY:20090605T170000Z/20090605T200000Z,20090605T223000Z/20
090606T003000Z
FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20090605T070000Z/20090605T150000Z,2009060
6T010000Z/20090606T070000Z
ORGANIZER:/principals/__uids__/ABC1234-53D8-4079-8392-01274F97F5E1/
END:VFREEBUSY
END:VCALENDAR
I've tried parsing it by using componentsSeparatedByString:#"\n", but there is a \n in part of the FREEBUSY data, causing it to not parse correctly.
Is there something easy that I'm missing?
The \n in the middle of FREEBUSY data is a part of the iCalendar spec; according to RFC 2445, the newline followed by a space is the correct way to split long lines, so you'll probably see a lot of this in scanning FREEBUSY data.
As Nathan suggests, an NSScanner may be all you need if the data you're expecting will be reasonably consistent. There are a number of vagaries in iCalendar, though, so I often find myself using libical to parse ics info. An quick-and-dirty example of parsing this data using libical:
NSString *caldata = #"BEGIN:VCALENDAR\nVERS....etc";
icalcomponent *root = icalparser_parse_string([caldata cStringUsingEncoding:NSUTF8StringEncoding]);
if (root) {
icalcomponent *c = icalcomponent_get_first_component(root, ICAL_VFREEBUSY_COMPONENT);
while (c) {
icalproperty *p = icalcomponent_get_first_property(c, ICAL_FREEBUSY_PROPERTY);
while (p) {
icalvalue *v = icalproperty_get_value(p);
// This gives: 20090605T170000Z/20090605T200000Z
// (note that stringWithCString is deprecated)
NSLog(#"FREEBUSY Value: %#", [NSString stringWithCString:icalvalue_as_ical_string(v)]);
icalparameter *m = icalproperty_get_first_parameter(p, ICAL_FBTYPE_PARAMETER);
while (m) {
// This gives: FBTYPE=BUSY
NSLog(#"Parameter: %#", [NSString stringWithCString:icalparameter_as_ical_string(m)]);
m = icalproperty_get_next_parameter(p, ICAL_FBTYPE_PARAMETER);
}
p = icalcomponent_get_next_property(c, ICAL_FREEBUSY_PROPERTY);
}
c = icalcomponent_get_next_component(root, ICAL_VFREEBUSY_COMPONENT);
}
icalcomponent_free(root);
}
Documentation for libical is in the project download itself (see UsingLibical.txt). There's also this lovely tutorial on shipping libical in your application bundle.
Take a look at NSScanner.
Related
Thank you for seeing this question.
I create a movie from GPUImageMovieComposition and GPUImageWriter, and sometimes (5% ~ 10 %) the movie has red frames at the beginning.
Please teach me why this phenomenon occur.
I use AVFileTypeMPEG4 as a sample filetype but AVFileTypeQuickTimeMovie is also same.
_movieFile = [[GPUImageMovieComposition alloc] initWithComposition:composition andVideoComposition:videoComposition andAudioMix:nil];
_movieFile.playAtActualSpeed = YES;
_movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:processedMovieURL
size:CGSizeMake(1280.0, 720.0)
fileType:AVFileTypeMPEG4
outputSettings:videoSetting];
_movieWriter.shouldPassthroughAudio = NO;
[_movieWriter setVideoInputReadyCallback:nil];
[_movieWriter setHasAudioTrack:YES audioSettings:audioSetting];
[_movieFile addTarget:_movieWriter];
_movieFile.audioEncodingTarget = _movieWriter;
[_movieFile enableSynchronizedEncodingUsingMovieWriter:_movieWriter];
[_movieWriter startRecording];
[_movieFile startProcessing];
SOLUTION
Finally I could find the way to solve... but not perfect way...
I modified
- (void)processMovieFrame:(CVPixelBufferRef)movieFrame withSampleTime:(CMTime)currentSampleTime
at GPUImageMovie.m little bit.
When currentSampleTime is set, all red frame has currentSampleTime.value == 0
so I avoided setting currentSampleTime when currentSampleTime.value == 0
Here are some codes which I actually used.
for (id<GPUImageInput> currentTarget in targets)
{
NSInteger indexOfObject = [targets indexOfObject:currentTarget];
NSInteger targetTextureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
if(currentSampleTime.value != 0){
[currentTarget newFrameReadyAtTime:currentSampleTime atIndex:targetTextureIndex];
}
}
In my case there was not red but just blank frames in the beginning of video recorded by GPUImageMovieWriter.
The problem was that the audio samples appears earlier then the video frames and assetWriter session has been started earlier then first video frames became available.
I've fix that by modifying processAudioBuffer function by replacing this code
if (CMTIME_IS_INVALID(startTime))
{
runSynchronouslyOnContextQueue(_movieWriterContext, ^{
if ((audioInputReadyCallback == NULL) && (assetWriter.status != AVAssetWriterStatusWriting))
{
[assetWriter startWriting];
}
[assetWriter startSessionAtSourceTime:currentSampleTime];
startTime = currentSampleTime;
});
}
on this
if (CMTIME_IS_INVALID(startTime))
{
NSLog(#"0: Had to drop an audio frame: %#", CFBridgingRelease(CMTimeCopyDescription(kCFAllocatorDefault, currentSampleTime)));
if (_shouldInvalidateAudioSampleWhenDone)
{
CMSampleBufferInvalidate(audioBuffer);
}
CFRelease(audioBuffer);
return;
}
The fix is applicable to the latest versions of GPUImage from May 27 to Jul 01, 2014.
The only solution I found so far that saved me from this issue is reverting back to commit e98cc813b.
I figured it out by using git bisect and performing batch of processing tests on the same video.
This commit already had all required functionality for my project, and only few changes were required to make it more stable. You can take a look at the changes here: https://github.com/crazyjooe/GPUImage.
Besides, after lots of testing, I can say that processing itself became much more stable, especially in terms of cancelling.
I wonder how video processing became less reliable and stable after all changes introduced.
As we know , we can use ASL (Apple System Logger) API in objective C to read logs and also with using asl_search it is possible to retrieve specific application logs.
But the problem is output of asl does not include the time when the log was created.
For example , when you open system.log in directory /var/log with Apple System Logger ,you see logs like this :
Nov 28 09:19:37 localhost bootlog[0]: BOOT_TIME 1354123177 0
But when quering asl with objective C , it reports every attribute of log except the time when the log is created, it means in example mentioned above , asl_search does not report Nov 28 09:19:37
Is there anyway to include time of creating logs with quering asl in objective C?
If this is not possible , what is another approach to retrieving time of logs?
This is my code , note that the time of logs which we can see in system.log files in /var/log does not appear in output of my code .
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
aslmsg q, m;
int i;
const char *key, *val;
q = asl_new(ASL_TYPE_QUERY);
asl_set_query(q, ASL_KEY_SENDER, "bootlog", ASL_QUERY_OP_EQUAL);
aslresponse r = asl_search(NULL, q);
while (NULL != (m = aslresponse_next(r)))
{
NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary];
for (i = 0; (NULL != (key = asl_key(m, i))); i++)
{
NSString *keyString = [NSString stringWithUTF8String:(char *)key];
val = asl_get(m, key);
NSString *string = [NSString stringWithUTF8String:val];
[tmpDict setObject:string forKey:keyString];
}
NSLog(#"%#", tmpDict);
}
aslresponse_free(r);
[pool drain];
return 0;
}
The timestamp of the log message is the value of the ASL_KEY_TIME key in the message. The value is a UNIX time (seconds since 1.1.1970).
You can convert the timestamp of the log message to NSDate with
NSDate *time = [NSDate dateWithTimeIntervalSince1970:(strtod(asl_get(m, ASL_KEY_TIME), NULL))];
If you're using Swift, there's a new open-source project called CleanroomASL that provides a type-safe API for reading and writing Apple System Log entries.
Querying the log returns the timestamp for each entry:
let client = ASLClient()
let query = ASLQueryObject()
client.search(query) { record in
if let record = record {
// we've gotten a log entry for the search.
// 'record' is of type ASLQueryObject.ResultRecord;
// you can access the timestamp as an NSDate
// using the record.timestamp property
}
else {
// there are no more log entries to process
}
// return true while we still want to get results
// or return false when we don't want to process more
return true
}
You can constrain your search query by setting additional query keys:
query.setQueryKey(.Message, value: nil, operation: .KeyExists, modifiers: .None)
query.setQueryKey(.Time, value: Int(NSDate().timeIntervalSince1970 - (60 * 60)), operation: .GreaterThanOrEqualTo, modifiers: .None)
The code above would return all ASL log entries recorded in the last 5 minutes that also have a value for the .Message attribute.
Note: On an iOS device, ASL will only return log entries made by your process. Also, logs are pruned rather aggressively, and may only include entries made by the current run of your application. If you want to persist your log entries beyond that, you may want to look into a more robust Swift logging package such as CleanroomLogger.
The differences between ASL behavior on the Mac/iOS Simulator and on an iOS device are explained further in the section Differences between the device and simulator on the CleanroomASL project page on GitHub.
Hi,
I would like to get the list of all available characters from the keyboard (Digital Alphabetical) in order to create a NSArray.
TISInputSourceRef source = TISCopyCurrentKeyboardInputSource();
NSLog(#"languages: %#", TISGetInputSourceProperty(source, kTISPropertyInputSourceLanguages));
NSLog(#"localized name: %#", TISGetInputSourceProperty(source, kTISPropertyLocalizedName));
I use these lines, but I can't find the right function to list the characters.
I also tried this line :
NSLog(#"List: %#", TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData));
Would you be able to get it from calling:
TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData);
That will return the 'uchr' data for the keyboard layout (if it exists), as a CFDataRef. You can read about the layout of the 'uchr' data here. You'll need to get the bytes from the CFDataRef, probably by calling something like CFDataGetBytes() or CFDataGetBytePtr().
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
Byte* buffer =
(Byte*) malloc (sizeof(Byte) * CFDataGetLength(uchr));
CFDataGetBytes(
uchr,
CFRangeMake(0,CFDataGetLength(uchr)),
buffer
);
How to read the information contained in the CFDataRef ?
I was wondering if there were a way to get back how far into an assembly a PKParser has parsed before encountering a syntax error.
reference: http://parsekit.com/
I'm using a grammar that basically describes a prefix notation expression language.
For example:
given your standard prefix notation expression grammar and a string "(+ a - b c))"
I'd like to retrieve that [(,+,a] where matched, so I can give the user some idea of where to look to fix their error, but the completeMatchFor and bestMatchFor don't return anything I can use to find this info.
Ideally I'd like to say that a '(' was expected, but it's not necessary for a grammar as simple as what I'm using.
From the book mentioned as the user manual, it seemed as if I would need to create a custom parser for this, but I was hoping that maybe I'd simply missed something in the framework.
Thoughts?
Developer of ParseKit here.
There are two features in ParseKit which can be used to help provide user-readable hints describing parse errors encountered in input.
-[PKParser bestMatchFor:]
The PKTrack class
It sounds like you're aware of the -bestMatchFor: method even if it's not doing what you expect in this case.
I think the PKTrack class will be more helpful here. As described in Metsker's book, PKTrack is exactly like PKSequence except that its subparsers are required, and an error is thrown (with a helpful error message) when all of its subparsers are not matched.
So here's a grammar for your example input:
#start = '(' expr ')' | expr;
expr = ('+' | '-') term term;
term = '(' expr ')' | Word;
Any productions listed contiguously are a Sequence -- but could instead be a Track.
The benefit of changing these Sequences to be Tracks is that an NSException will be thrown with a human-readable parse error message if the input doesn't match. The downside is that you must now wrap all usages of your factory-generated parser in a try/catch block to catch these Track exceptions.
The problem currently (or before now, at least) is that the PKParserFactory never produced a parser using Tracks. Instead, it would always use Sequences.
So I've just added a new option in head of trunk at Google Code (you'll need to udpate).
#define USE_TRACK 0
in
PKParserFactory.m
It's 0 by default. If you change this define to 1, Tracks will be used instead of Sequences. So given the grammar above and invalid input like this:
(+ a - b c))
and this client code:
NSString *g = // fetch grammar above
PKParser *p = [[PKParserFactory factory] parserFromGrammar:g assembler:self];
NSString *s = #"(+ a - b c))";
#try {
PKAssembly *res = [p parse:s];
NSLog(#"res %#", res);
}
#catch (NSException *exception) {
NSLog(#"Parse Error:%#", exception);
}
you will get a nice-ish human-readable error:
Parse Error:
After : ( + a
Expected : Alternation (term)
Found : -
Hope that helps.
I'm wrestling with this issue too. In order for -bestMatchFor: to be useful in identifying error conditions, there should be methods in PKAssembly's public interface indicating if there are more tokens/characters to be parsed. -completeMatchFor: is able to determine error state because it has access to the private -hasMore method. Perhaps PKAssembly's -hasMore method should be public.
I looked at PKTrack but since I want to handle errors programmatically, it wasn't useful to me.
My conclusion is I either write my own custom Track parser or I alter the framework and expose -hasMore. Are there other ways to handle errors?
Until I figure out a better way to detect errors, I've added the following to the file containing the implementation of my custom parser:
#interface PKAssembly ()
- (BOOL)hasMore;
- (id)peek;
#end
#implementation PMParser
...
#end
In my parse method:
PKAssembly* a = [PKTokenAssembly assemblyWithString:s];
PKAssembly* best = [self bestMatchFor:a];
PMParseNode* node = nil;
BOOL error = NO;
NSUInteger errorOffset = 0;
if (best == nil) // Anything recognized?
{
error = YES;
}
else
{
if ([best hasMore]) // Partial recognition?
{
PKToken* t = [best peek];
error = YES;
errorOffset = t.offset;
}
node = [best pop];
}
If an error occurred, errorOffset will contained the location of the unrecognized token.
I want to get the list of window titles of the currently running applications.
On windows I have EnumWndProc and GetWindowText.
On Linux I have XGetWindowProperty and XFetchName.
What is the Native Mac equivalent?
A few potentially useful references:
NSWindowList()
NSWorkspace -launchedApplications and +runningApplications
CGWindowListCreate() and CGWindowListCopyWindowInfo() (requires 10.5)
CGSGetWindowProperty()
CGSGetWindowProperty is not officially documented, but I believe you can use it with the an item of NSWindowList() as follows (completely untested):
OSErr err;
CGSValue titleValue;
char *title;
CGSConnection connection = _CGSDefaultConnection();
int windowCount, *windows, i;
NSCountWindows(&windowCount);
windows = malloc(windowCount * sizeof(*windows));
if (windows) {
NSWindowList(windowCount, windows);
for (i=0; i < windowCount; ++i) {
err = CGSGetWindowProperty(connection, windows[i],
CGSCreateCStringNoCopy("kCGSWindowTitle"),
&titleValue);
title = CGSCStringValue(titleValue);
}
free(windows);
}
In AppleScript, it's really easy:
tell application "System Events" to get the title of every window of every process
You can call applescript from within an application using NSAppleScript or use appscript as an ObjC-AppleScript bridge. With Leopard, you can use the Scripting Bridge (more untested code):
SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:#"com.apple.systemevents"];
SBElementArray *processes = [systemEvents processes];
for (SystemEventsProcess* process in processes) {
NSArray *titles = [[process windows] arrayByApplyingSelector:#selector(title)];
}
You could even try it in one long call, if you don't care about readability.
SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:#"com.apple.systemevents"];
NSArray *titles = [[[systemEvents processes]
arrayByApplyingSelector:#selector(windows)]
arrayByApplyingSelector:#selector(arrayByApplyingSelector:)
withObject:#selector(title)];
The compiler will complain that #selector(title) is the wrong type, but it should work. Hand roll some delegation and you could turn the call into [[[systemEvents processes] windows] title].
The CGSPrivate.h header that's floating around isn't directly compatible with OS X 10.8 in that CGSGetWindowProperty() no longer exists (well, it does, but you can't link to it anymore). So add these two lines to the CGSPrivate.h file -- I went ahead and figured this out myself after many hours searching Google -- to get it to work:
extern CGSConnection CGSDefaultConnectionForThread(void);
extern CGError CGSCopyWindowProperty(const CGSConnection cid, NSInteger wid, CFStringRef key, CFStringRef *output);
Adapting outis's code, here's a way of iterating through each window title. I have tested this with clang 4.2 on Mountain Lion:
CFStringRef titleValue;
CGSConnection connection = CGSDefaultConnectionForThread();
NSInteger windowCount, *windows;
NSCountWindows(&windowCount);
windows = (NSInteger*) malloc(windowCount * sizeof(NSInteger));
if (windows) {
NSWindowList(windowCount, windows);
for (int i = 0; i < windowCount; ++i)
{
CGSCopyWindowProperty(connection, windows[i], CFSTR("kCGSWindowTitle"), &titleValue);
if(!titleValue) //Not every window has a title
continue;
//Do something with titleValue here
}
free(windows);
}
Some other stuff I found out includes the following:
No window title exceeds 127 bytes.
Window titles are encoded with kCFStringEncodingMacRoman
So, if you want it as a C-string, write something like this:
char *cTitle[127] = {0};
CFStringGetCString(titleValue,cTitle,127,kCFStringEncodingMacRoman);
Personally, I'd recommend doing it this way since the Accessibility API is a total pain and requires extra permissions.
Hope this helps someone! Cheers!