Automatically call the migration script when the config does not look like v4
This commit is contained in:
parent
3e24b7170f
commit
ac335fcffa
|
@ -1,3 +1,5 @@
|
|||
# i3 config file (v4)
|
||||
#
|
||||
# This configuration file was written for the NEO layout. If you are using a
|
||||
# different layout, you should change it.
|
||||
|
||||
|
|
219
src/cfgparse.y
219
src/cfgparse.y
|
@ -5,9 +5,11 @@
|
|||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "all.h"
|
||||
|
||||
|
@ -46,6 +48,194 @@ int yywrap() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Goes through each line of buf (separated by \n) and checks for statements /
|
||||
* commands which only occur in i3 v4 configuration files. If it finds any, it
|
||||
* returns version 4, otherwise it returns version 3.
|
||||
*
|
||||
*/
|
||||
static int detect_version(char *buf) {
|
||||
char *walk = buf;
|
||||
char *line = buf;
|
||||
while (*walk != '\0') {
|
||||
if (*walk != '\n') {
|
||||
walk++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check for some v4-only statements */
|
||||
if (strncasecmp(line, "bindcode", strlen("bindcode")) == 0 ||
|
||||
strncasecmp(line, "# i3 config file (v4)", strlen("# i3 config file (v4)")) == 0 ||
|
||||
strncasecmp(line, "workspace_layout", strlen("workspace_layout")) == 0) {
|
||||
printf("deciding for version 4 due to this line: %.*s\n", (int)(walk-line), line);
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* if this is a bind statement, we can check the command */
|
||||
if (strncasecmp(line, "bind", strlen("bind")) == 0) {
|
||||
char *bind = strchr(line, ' ');
|
||||
if (bind == NULL)
|
||||
goto next;
|
||||
while ((*bind == ' ' || *bind == '\t') && *bind != '\0')
|
||||
bind++;
|
||||
if (*bind == '\0')
|
||||
goto next;
|
||||
if ((bind = strchr(bind, ' ')) == NULL)
|
||||
goto next;
|
||||
while ((*bind == ' ' || *bind == '\t') && *bind != '\0')
|
||||
bind++;
|
||||
if (*bind == '\0')
|
||||
goto next;
|
||||
if (strncasecmp(bind, "layout", strlen("layout")) == 0 ||
|
||||
strncasecmp(bind, "floating", strlen("floating")) == 0 ||
|
||||
strncasecmp(bind, "workspace", strlen("workspace")) == 0 ||
|
||||
strncasecmp(bind, "focus left", strlen("focus left")) == 0 ||
|
||||
strncasecmp(bind, "focus right", strlen("focus right")) == 0 ||
|
||||
strncasecmp(bind, "focus up", strlen("focus up")) == 0 ||
|
||||
strncasecmp(bind, "focus down", strlen("focus down")) == 0 ||
|
||||
strncasecmp(bind, "border normal", strlen("border normal")) == 0 ||
|
||||
strncasecmp(bind, "border 1pixel", strlen("border 1pixel")) == 0 ||
|
||||
strncasecmp(bind, "border borderless", strlen("border borderless")) == 0) {
|
||||
printf("deciding for version 4 due to this line: %.*s\n", (int)(walk-line), line);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
next:
|
||||
/* advance to the next line */
|
||||
walk++;
|
||||
line = walk;
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls i3-migrate-config-to-v4.pl to migrate a configuration file (input
|
||||
* buffer).
|
||||
*
|
||||
* Returns the converted config file or NULL if there was an error (for
|
||||
* example the script could not be found in $PATH or the i3 executable’s
|
||||
* directory).
|
||||
*
|
||||
*/
|
||||
static char *migrate_config(char *input, off_t size) {
|
||||
int writepipe[2];
|
||||
int readpipe[2];
|
||||
|
||||
if (pipe(writepipe) != 0 ||
|
||||
pipe(readpipe) != 0) {
|
||||
warn("migrate_config: Could not create pipes");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
warn("Could not fork()");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* child */
|
||||
if (pid == 0) {
|
||||
/* close writing end of writepipe, connect reading side to stdin */
|
||||
close(writepipe[1]);
|
||||
dup2(writepipe[0], 0);
|
||||
|
||||
/* close reading end of readpipe, connect writing side to stdout */
|
||||
close(readpipe[0]);
|
||||
dup2(readpipe[1], 1);
|
||||
|
||||
/* start the migration script, search PATH first */
|
||||
char *migratepath = "i3-migrate-config-to-v4.pl";
|
||||
execlp(migratepath, migratepath, NULL);
|
||||
|
||||
/* if the script is not in path, maybe the user installed to a strange
|
||||
* location and runs the i3 binary with an absolute path. We use
|
||||
* argv[0]’s dirname */
|
||||
char *pathbuf = strdup(start_argv[0]);
|
||||
char *dir = dirname(pathbuf);
|
||||
asprintf(&migratepath, "%s/%s", dir, "i3-migrate-config-to-v4.pl");
|
||||
execlp(migratepath, migratepath, NULL);
|
||||
|
||||
#if defined(__linux__)
|
||||
/* on linux, we have one more fall-back: dirname(/proc/self/exe) */
|
||||
char buffer[BUFSIZ];
|
||||
if (readlink("/proc/self/exe", buffer, BUFSIZ) == -1) {
|
||||
warn("could not read /proc/self/exe");
|
||||
exit(1);
|
||||
}
|
||||
dir = dirname(buffer);
|
||||
asprintf(&migratepath, "%s/%s", dir, "i3-migrate-config-to-v4.pl");
|
||||
execlp(migratepath, migratepath, NULL);
|
||||
#endif
|
||||
|
||||
warn("Could not start i3-migrate-config-to-v4.pl");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
/* parent */
|
||||
|
||||
/* close reading end of the writepipe (connected to the script’s stdin) */
|
||||
close(writepipe[0]);
|
||||
|
||||
/* write the whole config file to the pipe, the script will read everything
|
||||
* immediately */
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
if ((ret = write(writepipe[1], input + written, size - written)) < 0) {
|
||||
warn("Could not write to pipe");
|
||||
return NULL;
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
close(writepipe[1]);
|
||||
|
||||
/* close writing end of the readpipe (connected to the script’s stdout) */
|
||||
close(readpipe[1]);
|
||||
|
||||
/* read the script’s output */
|
||||
int conv_size = 65535;
|
||||
char *converted = malloc(conv_size);
|
||||
int read_bytes = 0;
|
||||
do {
|
||||
if (read_bytes == conv_size) {
|
||||
conv_size += 65535;
|
||||
converted = realloc(converted, conv_size);
|
||||
}
|
||||
ret = read(readpipe[0], converted + read_bytes, conv_size - read_bytes);
|
||||
if (ret == -1) {
|
||||
warn("Cannot read from pipe");
|
||||
return NULL;
|
||||
}
|
||||
read_bytes += ret;
|
||||
} while (ret > 0);
|
||||
|
||||
/* get the returncode */
|
||||
int status;
|
||||
wait(&status);
|
||||
if (!WIFEXITED(status)) {
|
||||
fprintf(stderr, "Child did not terminate normally, using old config file (will lead to broken behaviour)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int returncode = WEXITSTATUS(status);
|
||||
if (returncode != 0) {
|
||||
fprintf(stderr, "Migration process exit code was != 0\n");
|
||||
if (returncode == 2) {
|
||||
fprintf(stderr, "could not start the migration script\n");
|
||||
/* TODO: script was not found. tell the user to fix his system or create a v4 config */
|
||||
} else if (returncode == 1) {
|
||||
fprintf(stderr, "This already was a v4 config. Please add the following line to your config file:\n");
|
||||
fprintf(stderr, "# i3 config file (v4)\n");
|
||||
/* TODO: nag the user with a message to include a hint for i3 in his config file */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
void parse_file(const char *f) {
|
||||
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
|
||||
int fd, ret, read_bytes = 0;
|
||||
|
@ -160,6 +350,35 @@ void parse_file(const char *f) {
|
|||
}
|
||||
}
|
||||
|
||||
/* analyze the string to find out whether this is an old config file (3.x)
|
||||
* or a new config file (4.x). If it’s old, we run the converter script. */
|
||||
int version = detect_version(buf);
|
||||
if (version == 3) {
|
||||
/* We need to convert this v3 configuration */
|
||||
char *converted = migrate_config(new, stbuf.st_size);
|
||||
if (converted != NULL) {
|
||||
printf("\n");
|
||||
printf("****************************************************************\n");
|
||||
printf("NOTE: Automatically converted configuration file from v3 to v4.\n");
|
||||
printf("\n");
|
||||
printf("Please convert your config file to v4. You can use this command:\n");
|
||||
printf(" mv %s %s.O\n", f, f);
|
||||
printf(" i3-migrate-config-to-v4.pl %s.O > %s\n", f, f);
|
||||
printf("****************************************************************\n");
|
||||
printf("\n");
|
||||
free(new);
|
||||
new = converted;
|
||||
} else {
|
||||
printf("\n");
|
||||
printf("**********************************************************************\n");
|
||||
printf("ERROR: Could not convert config file. Maybe i3-migrate-config-to-v4.pl\n");
|
||||
printf("was not correctly installed on your system?\n");
|
||||
printf("**********************************************************************\n");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* now lex/parse it */
|
||||
yy_scan_string(new);
|
||||
|
||||
context = scalloc(sizeof(struct context));
|
||||
|
|
Loading…
Reference in New Issue