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);