]> git.ipfire.org Git - pakfire.git/commitdiff
file: Rewrite fixing interpreters
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 12 Sep 2023 17:17:07 +0000 (17:17 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 12 Sep 2023 17:17:07 +0000 (17:17 +0000)
The old version had some weaknesses in finding any arguments that came
after the interpreter which are being fixed here.

This version should also be easier to read.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/file.c

index 3358ebdfae364f476395d9803a64ffc0d65e5e10..7a994c48fc4b16f86a31e2b31f26569b4a96d8ea 100644 (file)
@@ -2418,129 +2418,147 @@ ERROR:
 
 static int pakfire_file_fix_interpreter(struct pakfire_file* file, const char* interpreter) {
        FILE* f = NULL;
-       char path[PATH_MAX];
        int r;
 
-       char* data = NULL;
+       char* buffer = NULL;
+       char* line = NULL;
        size_t length = 0;
+       char* p = NULL;
 
-       DEBUG(file->pakfire, "%s: Fixing interpreter %s\n",
-               pakfire_file_get_path(file), interpreter);
+       char command[PATH_MAX];
+       const char* args = NULL;
+
+       const char* path = pakfire_file_get_path(file);
+
+       DEBUG(file->pakfire, "%s: Fixing interpreter %s\n", path, interpreter);
 
        // Open the file
        f = pakfire_file_open(file);
        if (!f) {
-               ERROR(file->pakfire, "Could not open %s: %m\n", pakfire_file_get_path(file));
-               r = 1;
+               ERROR(file->pakfire, "Could not open %s: %m\n", path);
+               r = -errno;
                goto ERROR;
        }
 
-       // Read the entire content into the buffer
-       r = pakfire_read_file_into_buffer(f, &data, &length);
-       if (r) {
-               ERROR(file->pakfire, "Could not read file into memory: %m\n");
-               goto ERROR;
-       }
+       for (unsigned int lineno = 0;; lineno++) {
+               r = getline(&line, &length, f);
+               if (r < 0) {
+                       // We finished reading when we reach the end
+                       if (feof(f))
+                               break;
 
-       // Find the end of the first line
-       char* eol = memchr(data, '\n', length);
-       if (!eol) {
-               DEBUG(file->pakfire, "Could not find the end of the first line\n");
-               errno = EINVAL;
-               r = 0;
-               goto ERROR;
-       }
+                       ERROR(file->pakfire, "Could not read line from %s: %m\n", path);
+                       goto ERROR;
+               }
 
-       // Terminate the first line
-       *eol = '\0';
+               switch (lineno) {
+                       case 0:
+                               // Check if the first line starts with #!
+                               if (!pakfire_string_startswith(line, "#!")) {
+                                       r = -EINVAL;
+                                       goto ERROR;
+                               }
 
-       // Pointer to the beginning of the first line
-       char* p = data;
+                               // Remove the trailing newline
+                               pakfire_remove_trailing_newline(line);
 
-       // Pointer to the beginning of the rest of the script
-       const char* rest = eol + 1;
+                               // Start at the beginning of the line (after shebang)
+                               p = line + 2;
 
-       DEBUG(file->pakfire, "First line: %s\n", p);
+                               // Consume any whitespace
+                               while (*p && isspace(*p))
+                                       p++;
 
-       // Read shebang
-       if (pakfire_string_startswith(p, "#!")) {
-               p += 2;
-       } else {
-               ERROR(file->pakfire, "%s: Could not find shebang\n", pakfire_file_get_path(file));
-               errno = EINVAL;
-               r = 1;
-               goto ERROR;
-       }
+                               // Consume the old interpreter
+                               while (*p && !isspace(*p))
+                                       p++;
 
-       // Consume any whitespace
-       while (isspace(*p))
-               p++;
+                               // Consume any whitespace
+                               while (*p && isspace(*p))
+                                       p++;
 
-       // Consume the old interpreter
-       while (!isspace(*p))
-               p++;
+                               // The actual interpreter begins here
+                               interpreter = p;
 
-       // Consume any whitespace
-       while (isspace(*p))
-               p++;
+                               // Find the end of the interpreter
+                               while (*p && !isspace(*p))
+                                       p++;
 
-       // The command begins here
-       const char* command = p;
+                               // Terminate the interpreter
+                               *p++ = '\0';
 
-       // Find the end of the command
-       while (!isspace(*p))
-               p++;
+                               // Any arguments begin here (if we didn't consume the entire string, yet)
+                               if (*p)
+                                       args = p;
 
-       // Terminate the command
-       *p++ = '\0';
+                               ERROR(file->pakfire, "%s: Found command %s (%s)\n", path, interpreter, args);
 
-       DEBUG(file->pakfire, "%s: Found command: %s\n", pakfire_file_get_path(file), command);
+                               // Find the real path
+                               r = pakfire_which(file->pakfire, command, interpreter);
+                               if (r) {
+                                       ERROR(file->pakfire, "%s: Could not resolve %s: %m\n", path, interpreter);
+                                       goto ERROR;
+                               }
 
-       // Find the absolute path to the command
-       r = pakfire_which(file->pakfire, path, command);
-       if (r) {
-               DEBUG(file->pakfire, "%s: Could not resolve command %s: %m\n",
-                       pakfire_file_get_path(file), command);
-               goto ERROR;
-       }
+                               // If we could not resolve the command, this file has an invalid interpreter
+                               if (!*command) {
+                                       ERROR(file->pakfire, "%s: Could not find path for command %s\n", path, interpreter);
 
-       // If we could not resolve the command, this file has an invalid interpreter
-       if (!*path) {
-               file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
-               r = 0;
-               goto ERROR;
-       }
+                                       file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
+                                       r = 0;
+                                       goto ERROR;
+                               }
 
-       // Go back to the beginning of the file
-       rewind(f);
+                               // Write the new first line to the buffer
+                               r = asprintf(&buffer, "#!%s", command);
+                               if (r < 0)
+                                       goto ERROR;
 
-       // Write shebang
-       r = fprintf(f, (p >= rest) ? "#!%s\n" : "#!%s %s\n", path, p);
-       if (r < 0) {
-               ERROR(file->pakfire, "%s: Could not write back first line: %m\n",
-                       pakfire_file_get_path(file));
-               r = 1;
-               goto ERROR;
+                               // Append arguments if any
+                               if (args) {
+                                       r = asprintf(&buffer, "%s %s", buffer, args);
+                                       if (r < 0)
+                                               goto ERROR;
+                               }
+
+                               // Terminate the line
+                               r = asprintf(&buffer, "%s\n", buffer);
+                               if (r < 0)
+                                       goto ERROR;
+
+                               // Process the next line
+                               break;
+
+                       default:
+                               // Append the line to the buffer
+                               r = asprintf(&buffer, "%s%s", buffer, line);
+                               if (r < 0)
+                                       goto ERROR;
+                               break;
+               }
        }
 
-       // Determine the length of the payload
-       length -= rest - data;
+       // Go back to the beginning of the file
+       rewind(f);
 
-       // Write the rest
-       size_t bytes_written = fwrite(rest, 1, length, f);
-       if (bytes_written < length) {
-               ERROR(file->pakfire, "%s: Could not write the payload: %m\n",
-                       pakfire_file_get_path(file));
-               r = 1;
-               goto ERROR;
+       // Write back the buffer
+       if (buffer) {
+               size_t bytes_written = fwrite(buffer, 1, strlen(buffer), f);
+               if (bytes_written < length) {
+                       ERROR(file->pakfire, "%s: Could not write the payload: %m\n", path);
+                       r = -errno;
+                       goto ERROR;
+               }
        }
 
        // Success!
        r = 0;
 
 ERROR:
-       if (data)
-               free(data);
+       if (buffer)
+               free(buffer);
+       if (line)
+               free(line);
        if (f)
                fclose(f);