C equivalent to applescript "tell X to open Y" - c

Does anybody have a C library function that does the equivalent of AppleScript "Tell application X to open Y"? I'm not using cocoa, just plain old C, and ideally I would like to be able to do this from a commandl ine application that has no GUI interface.
This doesn't have to be fancy, it would be all right if I don't even wait for the success-or-failure response. Just needs to send the right Apple event.

I think by far the simplest solution would be to issue a system call to the osascript command line utility.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
system("osascript -e 'tell application \"X\" to open POSIX file \"/path/to/Y\"'");
return 0;
}

OP's kinda creating a rod for his own back here, as except for CoreFoundation most OS X-specific C APIs are now legacy/deprecated. Used to be you could call LaunchServices' LSOpenApplication(), but that got deprecated in 10.10.
If NSWorkspace isn't an option then use open via fork() and execv(), which allows arguments to be passed directly.

Thanks for the helpful thoughts, they really got me pointed in the right direction. Here's the synthesis of these answers that seems to be working well for me, code that assumes that the path and the filename will be variable:
char wdname[1000], obuf[1000];
const char* filename = "WavySwirl.stl";
getwd(wdname);
sprintf(obuf, "open %s/%s", wdname, filename);
system(obuf);

Related

Get highlighted text & open program with it (replicate clipboard.exe behavior)

Is it possible to highlight text with your cursor in any program, like you do with str+c and start a tool with the highlighted text as argument?
As far as I know, in Linux as well as in Windows, one can call a script/program with a custom shortcut. I assume str+c just does the same, calling a little program with the highlighted text as argument. How to replicate this?
For demonstration purposes, let's take this C - program printing the value it was called with:
#include <stdio.h>
int main(int argc, char**argv){
if(argc == 2){
printf("program called with: '%s'\n", argv[1]);
}
}
Can one type the text "HelloWorld" in Word for example, highlight it, and press something like str+alt+p, calling
someprogram.exe HelloWorld
or as for Linux
someprogram HelloWorld
I am really curious if this is possible.
Edit: I'm interested to know, how to replicate the clipboard.exe functionality. I have written a program "write_custom.exe" storing anything given as argument (argv[1]) in a text-file, after deleting it's previous content. Other programs can read the content of this text-file and so are able to use this custom clipboard. It's purpose is mainly for self-teaching.
As I am at the beginning of my codeing career, I only know C, but I am open for solutions in other languages as well. My goal is to run this write_custom.exe, with highlighted text as argument, on my computer and my Linux-VM.
You might want to check out ncurses (Linux) and Console API (Windows). The code will not be cross-platform, but you can pretty easily write some code to make them share some basic behavior :).

Mac sandbox: running a binary tool that needs /tmp

I have a sandboxed Cocoa app that, during an export process, needs to run a third party command-line tool. This tool appears to be hardcoded to use /tmp for its temporary files; sandboxing doesn't permit access to this folder, so the export fails.
How can I get this tool to run? I don't have access to its source code, so I can't modify it to use NSTemporaryDirectory(), and it doesn't appear to respect the TMP or TEMPDIR environment variables. For reasons I don't understand, giving myself a com.apple.security.temporary-exception.files.absolute-path.read-write entitlement doesn't seem to work, either.
Is there some way to re-map folders within my sandbox? Is there some obscure trick I can use? Should I try to patch the tool's binary somehow? I'm at my wit's end here.
I was able to get user3159253's DYLD_INSERT_LIBRARIES approach to work. I'm hoping they will write an answer describing how that works, so I'll leave the details of that out and explain the parts that ended up being specific to this case.
Thanks to LLDB, elbow grease, and not a little help from Hopper, I was able to determine that the third-party tool used mkstemp() to generate its temporary file names, and some calls (not all) used a fixed template starting with /tmp. I then wrote a libtmphack.dylib that intercepted calls to mkstemp() and modified the parameters before calling the standard library version.
Since mkstemp() takes a pointer to a preallocated buffer, I didn't feel like I could rewrite a path starting with a short string like "/tmp" to the very long string needed to get to the Caches folder inside the sandbox. Instead, I opted to create a symlink to it called "$tmp" in the current working directory. This could break if the tool chdir()'d at an inopportune time, but fortunately it doesn't seem to do that.
Here's my code:
//
// libtmphack.c
// Typesetter
//
// Created by Brent Royal-Gordon on 8/27/14.
// Copyright (c) 2014 Groundbreaking Software. This file is MIT licensed.
//
#include "libtmphack.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
//#include <errno.h>
#include <string.h>
static int gbs_has_prefix(char * needle, char * haystack) {
return strncmp(needle, haystack, strlen(needle)) == 0;
}
int mkstemp(char *template) {
static int (*original_mkstemp)(char * template) = NULL;
if(!original_mkstemp) {
original_mkstemp = dlsym(RTLD_NEXT, "mkstemp");
}
if(gbs_has_prefix("/tmp", template)) {
printf("libtmphack: rewrote mkstemp(\"%s\") ", template);
template[0] = '$';
printf("to mkstemp(\"%s\")\n", template);
// If this isn't successful, we'll presume it's because it's already been made
symlink(getenv("TEMP"), "$tmp");
int ret = original_mkstemp(template);
// Can't do this, the caller needs to be able to open the file
// int retErrno = errno;
// unlink("$tmp");
// errno = retErrno;
return ret;
}
else {
printf("libtmphack: OK with mkstemp(\"%s\")\n", template);
return original_mkstemp(template);
}
}
Very quick and dirty, but it works like a charm.
Since #BrentRoyal-Gordon has already published a working solution I'm simply duplicating my comment which inspired him to produce the solution:
In order to fix a program behavior, I would intercept and override some system calls with the help of DYLD_INSERT_LIBRARIES and a custom shared library with a custom implementation of the given system calls.
The exact list of the syscalls which need to be overridden depends on nature of the application and can be studied with a number of tools built upon MacOS DTrace kernel facility. E.g. dtruss or Hopper. #BrentRoyal-Gordon has investigated that the app can be fixed solely with an /appropriate/ implementation of mkstemp.
That's it. I'm still not sure that I've deserved the bounty :)
Another solution would be to use chroot within the child process (or posix_spawn options) to change its root directory to a directory that is within your sandbox. Its “/tmp” will then be a “tmp” directory within that directory.

Simple C Wrapper Around OpenSSH (with Cygwin on Windows)

I am packaging a program on Windows that expects to be able to externally call OpenSSH. So, I need to package ssh.exe with it and I need to force ssh.exe to always be called with a custom command line parameter (specifically -F to specify a config file it should use). There is no way to force the calling program to do this, and there are no simple ways to do this otherwise in Windows (that I can think of anyway - symlinks or cmd scripts won't work) so I was just going to write a simple wrapper in C to do it.
This is the code I put together:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int ret;
char **newv = malloc((argc + 2) * sizeof(*newv));
memmove(newv, argv, sizeof(*newv) * argc);
newv[argc] = "-F ssh.conf";
newv[argc+1] = 0;
ret = execv("ssh.orig.exe", newv);
printf("execv failed with return value of %i", ret);
return ret;
}
I then compile this code using GCC 4.6.3 in Cygwin and it runs without error; however, there is a strange behavior with regards to input and output. When you go to type at the console (confirming the authenticity of the host and entering in a password, etc) only part of the input appears on the console. For example, if I type in the word 'yes' and press enter, only the 'e' will appear on the console and SSH will display an error about needing to type 'yes' or 'no'. Doing this from the Windows command prompt will result in your input going back to the command propmt, so when you type 'yes' and press enter, you get the ''yes' is not recognized as an internal or external command...' message as if the input were being typed at the command prompt. Eventually SSH will time out after that.
So, I'm obviously missing something here, and I'm assuming it has something to do with the way execv works (at least the POSIX Cygwin version of it).
Is there something I'm missing here or are there any alternatives? I was wondering if maybe I need to fork it and redirect the I/O to the fork (although fork() doesn't seem to work - but there are other issues there on Windows). I tried using _execv from process.h but I was having issues getting the code right for that (also could have been related to trying to use gcc).
It's also possible that there may be a non-programming way to do this that I haven't thought of, but all of the possibilities I've tried don't seem to work.
Thoughts?
I ended up finding a solution to this problem. I'm sure there were other ways to do this, but this seems to fix the issue and works well. I've replaced the execv line with the following code:
ret = spawnv(P_WAIT, "ssh.orig.exe", newv);
You have to use 'P_WAIT' otherwise the parent process completes and exits and you still have the same problem as before. This causes the parent process to wait, but still transfers input and output to the child process.

C (or embedded script) function that will open a .txt file visually

I am writing a program that writes to a .txt file but with a separate function I would like to open the file visually (in the default text editor).
I want the function to do the same as double clicking a file...
Not opening a file just to edit it in the code (not with fopen()) but actually view the file in a separate window!
Cross-Platform if possible.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
system("exec programname filename");
return 0;
}
This works on linux, hope that helps. Im not quite sure how you could tell which editor is default and open that up though.
If you were programming this hypothetical function in C on Windows, you could do something like
system ("notepad myfile.txt");
There is no way to do this cross-platform.
If a user double-clicks on a file, then the OS takes over and checks to see which application has been associated with the file type. Since this is an OS specific activity, it differs in implementation from OS to OS.
To do it you would need to query the OS for the application to launch with a system call. This is OS-specific though.

Using popen() to open a program in command line?

Is it possible to open a program using another program? For example:
I want to make a command line application in C that will prompt the user to type in the name of a program (lets say Microsoft Word.app), and that program will launch. Would I do something like this:
#include <stdio.h>
#include <time.h>
int main (int argc, const char * argv[]) {
char programName[1000];
printf("Type in the name of the program you would like to open: ");
scanf("%s", programName);
popen(programName);
}
However, popen() asks me for another char. How would I go about using popen() to open the program?
EDIT: The following code works!
#include <stdio.h>
#include <time.h>
int main (int argc, const char * argv[]) {
char programName[1000];
char app[100] = ".app";
char openApp[100] = "open /Applications/";
printf("Type in the name of the program you would like to open: ");
scanf("%s", programName);
strcat(openApp, programName);
strcat(openApp, app);
system(openApp);
}
popen lets you launch a program and get a file descriptor to its input or output, much like fopen works for files. For instance, if you wanted to read the output of your program, you'd use popen("program", "r"). On the other hand, if you want to write to its input, you would use popen("program", "w"). Mac OS X also allows for r+, which lets you read the output and write to the input but this capability isn't standard and shouldn't be relied on for cross-platform code.
If you just want to launch a program, you might as well use the system function, which does that and waits until the program exits, at which point it returns the status code. system actually invokes the shell to work, so arguments will undergo expansion (environment variables, ~, etc).
EDIT Following your comment that system("Microsoft Word.app") doesn't work as you'd expect: there are several reasons for this, actually. Starting with the message you get: this is because what you wrote is equivalent to opening a terminal window and typing Microsoft Word.app. In other words, it tries to find a program called "Microsoft", then pass it the argument "Word.app". You would need to either quote the program name or escape spaces to have the shell understand it's a whole program name and not a program name then an argument: system("Microsoft\ Word.app")
Now, this should complain saying that the shell can't find the program "Microsoft Word.app", which is already a step forward.
This is because on Mac OS, app files aren't executable files: they're folders that the Finder displays as a single file. You can verify that by ctrl+clicking (or right-clicking) an app and selecting "Show package contents" (this will open the app folder). The actual executable for Microsoft Word.app must be somewhere along the path of Microsoft Word.app/Contents/MacOS/Microsoft Word.
As you can see, this is getting kind of complex. Luckily enough, Apple provides the open executable, which can use a bunch of OS services to figure out those details. It allows to launch applications in the following fashion:
open -a Microsoft\ Word
This should launch Word. (Notice how you still need to escape the spaces.) In pure C code, that would get you something like this:
system("open -a Microsoft\\ Word");
If you choose to use Objective-C and Cocoa, however, there is a very simple way to open applications:
NSString* appName = #"Microsoft Word"; // no escape!
[[NSWorkspace sharedWorkspace] launchApplication:appName];
NSString objects can be created from C string easily enough:
NSString* appName = [[NSString alloc] initWithCString:programName encoding:NSUTF8StringEncoding];
[[NSWorkspace sharedWorkspace] launchApplication:appName];
[appName release];
It would be better to use system(3) for this purpose.
The popen(3) function establishes a pipeline that can be read or written by the caller. But GUI applications do not use standard input and standard output, they connect to the graphical interface server, sometimes called the "window server". This server is already running and already has decided what its keyboard input will be, and it is always writing its output to the video device.
To start a .app you should actually run the open(1) program, so try something like:
system("open /Applications/MacVim.app");

Resources