PDA

View Full Version : manual edit of file works but creation via writeToFile: not working




prostuff1
Apr 9, 2012, 08:30 PM
So a little bit of a a story:

I am writing myself a little helper application that edits a file (ui.properties) that belongs to the application CrashPlan.

I have CrashPlan installed on a headless linux server and to be able to connect to it I need to edit the above ui.properties file (located at "/Applications/CrashPlan.app/Contents/Resources/Java/conf/ui.properties) and insert the line servicePort=4200. I then set up a tunnel from my Mac to the remote linux server and launch CrashPlan on my Mac.

I have an appleScript that does the above and while it works, it is a little limited as I can not specify which linux server the ssh tunnel is supposed to go to. The idea behind this little helper app was so that I could specify a few other options.

If I open the above ui.properties file in textmate, make the changes, close it, and launch CrashPlan everything works fine. If I use the appleScript I have it works fine. The issue I am having is that using the code I have that changes the file to what looks to be correct it does not seem to work.

In the code below I am basically reading in the contents of the file, changing what I need to, deleting the old file, writing the changed stuff to a new file with the same name as the old one. It is not pretty right now but I will clean it up later.
I have:

filesContent = [[NSString alloc] initWithContentsOfFile:crashplanUIFile];
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
NSLog(@"files content = %@", filesContent);
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");

//NSString *newFilesContent = [[filesContent stringByAppendingString:@"servicePort=4200"] stringByAppendingFormat:@"\n"];
NSString *newFilesContent = [filesContent stringByReplacingOccurrencesOfString:@"#servicePort=4243" withString:@"servicePort=4200"];
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
NSLog(@"New files content = %@", newFilesContent);
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");

NSError **error;

if([[NSFileManager defaultManager] removeItemAtPath:crashplanUIFile error:error])
{
[newFilesContent writeToFile:crashplanUIFile atomically:TRUE encoding:NSUnicodeStringEncoding error:error];
NSDictionary *attributes;
NSNumber *permissions;
permissions = [NSNumber numberWithUnsignedLong: 444];
attributes = [NSDictionary dictionaryWithObject:permissions forKey:NSFilePosixPermissions];
// This actually sets the permissions
[[NSFileManager defaultManager] setAttributes:attributes ofItemAtPath:crashplanUIFile error:error];
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
NSLog(@"Wrote file!!");
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}



The appleScript code looks like this:

set theFile to "/Applications/CrashPlan.app/Contents/Resources/Java/conf/ui.properties" --give it the correct path to the file
try
set fileRef to (open for access (POSIX file theFile) with write permission) --open the file to for which we specified the path
on error errMsg number errNum
display dialog ("Open for Access, Error Number: " & errNum as string) & return & errMsg --handle any errors we have
end try

set filesEOF to get eof fileRef -- find the end of the file

try
set dataIn to read fileRef for filesEOF --setting point to insert the line at
on error errMsg number errNum
display dialog ("Read, Error Number: " & errNum as string) & return & errMsg --handle any errors
end try
set whereItIs to offset of "servicePort=4200" in dataIn --setting up the line to add/remove


if whereItIs = 0 then --the line does not exist
set dataOut to dataIn & ASCII character 10 & "servicePort=4200" & return --add the line to the end of the file
set msgToUser to "servicePort=4200 added" --set the message to display to the user
set determineActivate to true
else
copy characters 1 through (whereItIs - 1) of dataIn to dataOut -- the line does exist so "count backwards" and remove it
set msgToUser to "servicePort=4200 removed" --set the message that it was removed
set determineActivate to false
set startPoint to whereItIs + 16 -- "servicePort=4200"&return is 17 characters less one is 16
if startPoint < filesEOF then
set dataOut to dataOut & (characters startPoint thru filesEOF of dataIn)
end if
end if

set dataOut to dataOut as text --setting the file again to the correct output

set eof of fileRef to 0

try
write dataOut to fileRef -- writing the data to the file
on error errMsg number errNum
display dialog ("Write, Error Number: " & errNum as string) & return & errMsg --check for any errors
end try


set eof of fileRef to (length of dataOut) --finding the end of the file


try
close access fileRef --close the file we opened
on error errMsg number errNum
display dialog ("Close, Error Number: " & errNum as string) & return & errMsg
end try



Sydde
Apr 10, 2012, 12:12 AM
IIRC, NSStringUnicodeEncoding is UTF16, is that what you want? Is that what you are getting (what does the file look like in hex view)? Try NSStringUTF8Encoding, see if that works.

Also, I would use -initWithContentsOfFile:encoding:error:, as the method you are using is deprecated.

prostuff1
Apr 10, 2012, 08:15 AM
IIRC, NSStringUnicodeEncoding is UTF16, is that what you want? Is that what you are getting (what does the file look like in hex view)? Try NSStringUTF8Encoding, see if that works.

Also, I would use -initWithContentsOfFile:encoding:error:, as the method you are using is deprecated.

Well, that seems to have done the trick. I changes the -initWith part and the Unicode (NSUTF8StringEncoding) and it seems to work now.

Now to get -applicationWillTerminate: working so I can change the file contents back to what they were before the modification.

Sydde
Apr 10, 2012, 11:04 AM
You could just use a renaming convention. At launch, look for ".ui.properties", a hidden version of the file your app created: if it exists, rename ui.properties to ".def.ui.properties" and rename ".ui.properties" to "ui.properties". Alternately, if ".def.ui.properties" exists, do nothing (the existing version is the one you want because your app failed to quit correctly). If no hidden file exists, create your changes and rename ui.properties to .def.ui.properties before saving to ui.properties.

At -applicationWillTerminate:, perform the appropriate rename swap. That way, you only actually have to write the changed file out once.

chown33
Apr 10, 2012, 12:25 PM
filesContent = [[NSString alloc] initWithContentsOfFile:crashplanUIFile];
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
NSLog(@"files content = %@", filesContent);
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");

//NSString *newFilesContent = [[filesContent stringByAppendingString:@"servicePort=4200"] stringByAppendingFormat:@"\n"];
NSString *newFilesContent = [filesContent stringByReplacingOccurrencesOfString:@"#servicePort=4243" withString:@"servicePort=4200"];
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
NSLog(@"New files content = %@", newFilesContent);
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");

NSError **error;

if([[NSFileManager defaultManager] removeItemAtPath:crashplanUIFile error:error])
{
[newFilesContent writeToFile:crashplanUIFile atomically:TRUE encoding:NSUnicodeStringEncoding error:error];
NSDictionary *attributes;
NSNumber *permissions;
permissions = [NSNumber numberWithUnsignedLong: 444];
attributes = [NSDictionary dictionaryWithObject:permissions forKey:NSFilePosixPermissions];
// This actually sets the permissions
[[NSFileManager defaultManager] setAttributes:attributes ofItemAtPath:crashplanUIFile error:error];
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
NSLog(@"Wrote file!!");
NSLog(@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}



The red-hilited declaration using NSError is definitely wrong. If you're not going to use the returned-by-reference NSError*, then you're better off passing nil instead of an uninitialized pointer. You should read the Error Handling Programming Guide. At a minimum, read the section on "Using and Creating Error Objects", focusing on the ones that indirectly return NSError objects.

The red-hilited 444 constant is probably wrong. If you intend to apply read-only permissions for all (user, group, other), the correct constant is 0444 (an octal constant). If you intend something else, please describe that intent. As given, 444 is a decimal constant, which is 0674 octal. I doubt that group-executable is useful, though the other permission-bits seem reasonable (user r/w, group r/w, others read-only).

I also see that you're not checking the return value of writeToFile:, which is a boolean. It's nonsense to apply permissions if the write failed, so a conditional is called for.

prostuff1
Apr 12, 2012, 04:41 PM
Thanks for the pointer I truly do appreciate it.

As I mentioned it is not pretty nor complete. I was just trying to get some working code and the UTF8 comment made that happen. I have since done some more reading and had corrected the 444 permissions.

The input on the NSError stuff is much appreciated. I will do some reading and see about changing/correcting that.