-3

I would like to find out for my C++ program, which .pdf browser is installed. I expected to use popen, but even this code results in returning a nun-NULL

FILE *fp;
fp = popen("abracadabraxx ", "r");

I expected one cannot fork if the application is not installed. Do I misunderstood something?

katang
  • 2,474
  • 5
  • 24
  • 48
  • 3
    Yes, you misunderstood a couple of things. Such as the basic fact that something like this entirely depends on the operating system, and since you failed to state what it is, you're out of luck and noone will be able to help you. This is a completely different process depending upon one uses Windows, Linux, or Mac-OSx, and, typically, something like this is done by checking the installed O/S's application registry. – Sam Varshavchik Jan 01 '19 at 18:18
  • 2
    This looks like a [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). `popen` does not by itself give you the information which PDF viewer is used. – Werner Henze Jan 01 '19 at 18:23
  • Do nun-NULLs wear tunics? – Matteo Italia Jan 01 '19 at 18:24
  • 1
    Not sure, but I hear they can be habit-forming. – user4581301 Jan 01 '19 at 18:27
  • 1
    @WernerHenze To be fair, attempting to run something is not a bad way to find out whether it can be run. Let's try not to overinvoke XY. – Lightness Races in Orbit Jan 01 '19 at 18:29
  • @LightnessRacesinOrbit though `popen` will succeed if the command is not found. – spectras Jan 01 '19 at 18:32

2 Answers2

6

popen opens a pipe. And:

if the fork(2) or pipe(2) calls fail, or if the function cannot allocate memory, NULL is returned.

Neither of these conditions have anything to do with the process to which you're opening a pipe. They are about the pipe itself.

To determine whether the process succeeded, you need to proceed to the pclose stage and:

pclose(): on success, returns the exit status of the command; if wait4(2) returns an error, or some other error is detected, -1 is returned.

You're trying to capture the event that the shell returns an exit code of 127, which you can get via WEXITSTATUS.

Example:

#include <cstdio>
#include <iostream>
#include <sys/wait.h>

int main()
{
    FILE* fd = popen("nonexistingfoobar", "r");
    const int res = pclose(fd);
    printf("%d\n", WEXITSTATUS(res));
}

(live demo)

Always read the documentation!

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • The thing is, if the command does not exist, it is still a success. `FILE * fd = popen("nonexistingfoobar", "r"); printf("%d\n", pclose(fd));` does not print -1. From `popen`/`pclose` PoV, the shell command has run successfuly. It just happened to print *"sh: 1: nonexistingfoobar: not found"* on its stderr, which is not an error per se. – spectras Jan 01 '19 at 18:36
  • @spectras No, it gives you the exit code of the shell command, just like with any other shell work. [Use WEXITSTATUS and you'll find the value is 127](https://stackoverflow.com/a/20465094/560648), which is [the command-not-found condition the OP wants to captture](https://stackoverflow.com/q/1763156/560648). I've added some more detail to the answer for those without research capability ;) – Lightness Races in Orbit Jan 01 '19 at 18:42
  • That's indeed where I was leading: in `pclose` PoV, it was a success, so you don't get a `pclose` error, you get the result — a message on `fd` and an exit code — which you should then inspect. – spectras Jan 01 '19 at 18:47
  • @Lightness Races in Orbit I have read the documentation, but did not expect that I shall ask plose() if I want to know an answer from popen(), BTW: Is this a proper method for finding out if an application is installed? – katang Jan 01 '19 at 19:04
  • @katang I'm not sure about "proper" - it seems a bit of a hack, and it's not terribly portable. But as I said in comments under the question, if you want to `popen` to a particular command anyway, and need to detect when this fails _because_ the command is unavailable, this is a reasonable way to do it. Ultimately the "proper" method entirely depends on your circumstances. – Lightness Races in Orbit Jan 01 '19 at 20:15
1

I expected one cannot fork if the application is not installed

As per documentation, popen starts the shell (using /bin/sh -c on POSIX systems), and returns you a file descriptor connected to the shell output (or input, depending on the second parameter). The validity of the command is irrelevant to the success of popen "per se", so you are not going to get NULL in output (which is to be expected for lower-level failures, such as impossibility to fork, to create file descriptors or to exec the shell itself, all stuff that "shouldn't happen" unless in low-resources situations).

You'll just get whatever message the shell will spit out on standard output (probably none, as it will be on standard error); incidentally, this is one of the reasons why generally popen is not a good idea if you need decent diagnostics of what went wrong.

What you can do, however, is to inspect the return code of pclose, which will provide you the shell exit code, that you may use to find out some details about what went wrong (if any).


That being said, "launching" a file according to the user preferences is generally best delegated to the operating system or to OS-supplied utilities; easy ways to do so are the ShellExecute API on Windows, the open utility on macOS, and the xdg-open utility on most Linux installations1.

As for getting the executable to be used to start a file, it's a way more complicated matter. I don't know about macOS, but on Windows a file may be associated to something more complicated than a "plain" executable (possibly with a command line) - there used to be DDE commands, some new fancy MSI abominations that auto-install stuff on demand and other "magic" shell stuff; I suspect that even on Linux there will be some broken support for DBus activation of applications and stuff like that; so, in general there's no such a thing as an "associated executable", just ask the system to open the file and stay out of this kind of trouble.


  1. As always happens, Linux "desktop" stuff is way more fragmented, complicated and unreliable than it needs to be, but xdg-open is generally a safe-ish bet (although I saw it fail as well). Don't go down Firefox's path of trying to do this stuff on its own, or you too will have 10 years old bugs on wrong applications being started that periodically resurface.
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299