+ char *out, *err;
+ int r, freeit = 0;
+ char *buf;
+ int len;
+
+ NEED_ROOT_OR_IS_DEVICE (path, NULL);
+ ABS_PATH (path, NULL);
+
+ if (strncmp (path, "/dev/", 5) == 0)
+ buf = (char *) path;
+ else {
+ buf = sysroot_path (path);
+ if (!buf) {
+ reply_with_perror ("malloc");
+ return NULL;
+ }
+ freeit = 1;
+ }
+
+ /* file(1) manpage claims "file returns 0 on success, and non-zero on
+ * error", but this is evidently not true. It always returns 0, in
+ * every scenario I can think up. So check the target is readable
+ * first.
+ */
+ if (access (buf, R_OK) == -1) {
+ if (freeit) free (buf);
+ reply_with_perror ("access: %s", path);
+ return NULL;
+ }
+
+ r = command (&out, &err, "file", "-zbsL", buf, NULL);
+ if (freeit) free (buf);
+
+ if (r == -1) {
+ free (out);
+ reply_with_error ("file: %s: %s", path, err);
+ free (err);
+ return NULL;
+ }
+ free (err);
+
+ /* We need to remove the trailing \n from output of file(1). */
+ len = strlen (out);
+ if (out[len-1] == '\n')
+ out[len-1] = '\0';
+
+ return out; /* caller frees */
+}
+
+/* zcat | file */
+char *
+do_zfile (char *method, char *path)
+{
+ int len;
+ const char *zcat;
+ char *cmd;
+ FILE *fp;
+ char line[256];
+
+ NEED_ROOT (NULL);
+ ABS_PATH (path, NULL);
+
+ if (strcmp (method, "gzip") == 0 || strcmp (method, "compress") == 0)
+ zcat = "zcat";
+ else if (strcmp (method, "bzip2") == 0)
+ zcat = "bzcat";
+ else {
+ reply_with_error ("zfile: unknown method");
+ return NULL;
+ }
+
+ if (asprintf_nowarn (&cmd, "%s %R | file -bsL -", zcat, path) == -1) {
+ reply_with_perror ("asprintf");
+ return NULL;
+ }
+
+ if (verbose)
+ fprintf (stderr, "%s\n", cmd);
+
+ fp = popen (cmd, "r");
+ if (fp == NULL) {
+ reply_with_perror ("%s", cmd);
+ free (cmd);
+ return NULL;
+ }
+
+ free (cmd);
+
+ if (fgets (line, sizeof line, fp) == NULL) {
+ reply_with_perror ("zfile: fgets");
+ fclose (fp);
+ return NULL;
+ }
+
+ if (fclose (fp) == -1) {
+ reply_with_perror ("zfile: fclose");
+ return NULL;
+ }
+
+ len = strlen (line);
+ if (len > 0 && line[len-1] == '\n')
+ line[len-1] = '\0';
+
+ return strdup (line);