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
|
# This configuration file was written for the NEO layout. If you are using a
|
||||||
# different layout, you should change it.
|
# different layout, you should change it.
|
||||||
|
|
||||||
|
219
src/cfgparse.y
219
src/cfgparse.y
@ -5,9 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
@ -46,6 +48,194 @@ int yywrap() {
|
|||||||
return 1;
|
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) {
|
void parse_file(const char *f) {
|
||||||
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
|
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
|
||||||
int fd, ret, read_bytes = 0;
|
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);
|
yy_scan_string(new);
|
||||||
|
|
||||||
context = scalloc(sizeof(struct context));
|
context = scalloc(sizeof(struct context));
|
||||||
|
Loading…
Reference in New Issue
Block a user