+
+/* Read an unsigned little endian short at a specified offset in a file.
+ * Returns a non-negative int on success or -1 on failure.
+ */
+static int
+read_leshort (FILE* fp, int offset)
+{
+ char buf[2];
+ if (fseek (fp, offset, SEEK_SET) != 0 ||
+ fread (buf, sizeof(char), 2, fp) != 2)
+ {
+ return -1;
+ }
+ return ((buf[1] & 0xFF) << 8) | (buf[0] & 0xFF);
+}
+
+/* Extract the kernel version from a Linux kernel file.
+ * Returns a malloc'd string containing the version or NULL if the
+ * file can't be read, is not a Linux kernel, or the version can't
+ * be found.
+ *
+ * See ftp://ftp.astron.com/pub/file/file-<ver>.tar.gz
+ * (file-<ver>/magic/Magdir/linux) for the rules used to find the
+ * version number:
+ * 514 string HdrS Linux kernel
+ * >518 leshort >0x1ff
+ * >>(526.s+0x200) string >\0 version %s,
+ *
+ * Bugs: probably limited to x86 kernels.
+ */
+static char*
+get_kernel_version (char* filename)
+{
+ FILE* fp;
+ int size = 132;
+ char buf[size];
+ int offset;
+
+ fp = fopen (filename, "rb");
+
+ if (fseek (fp, 514, SEEK_SET) != 0 ||
+ fgets (buf, size, fp) == NULL ||
+ strncmp (buf, "HdrS", 4) != 0 ||
+ read_leshort (fp, 518) < 0x1FF)
+ {
+ /* not a Linux kernel */
+ fclose (fp);
+ return NULL;
+ }
+
+ offset = read_leshort (fp, 526);
+ if (offset == -1)
+ {
+ /* can't read version offset */
+ fclose (fp);
+ return NULL;
+ }
+
+ if (fseek (fp, offset + 0x200, SEEK_SET) != 0 ||
+ fgets (buf, size, fp) == NULL)
+ {
+ /* can't read version string */
+ fclose (fp);
+ return NULL;
+ }
+
+ fclose (fp);
+
+ buf[strcspn (buf, " \t\n")] = '\0';
+ return strdup (buf);
+}