i3 - improved tiling WM


Remove references to PATH_MAX macro

Patch status: needinfo

Patch by Lancelot SIX

Long description:

Since the macro PATH_MAX is not defined on every system (GNU/Hurd being
one of those who do not define it), we remove all references to this
macro. Instead, we use a buffer of arbitraty size and grow it when
needed to contain paths.

To apply this patch, use:
curl http://cr.i3wm.org/patch/309/raw.patch | git am

b/i3-nagbar/main.c

23
@@ -159,8 +159,9 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
24
     fclose(script);
25
 
26
     char *link_path;
27
+    char *exe_path = get_exe_path(argv0);
28
     sasprintf(&link_path, "%s.nagbar_cmd", script_path);
29
-    symlink(get_exe_path(argv0), link_path);
30
+    symlink(exe_path, link_path);
31
 
32
     char *terminal_cmd;
33
     sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
34
@@ -172,6 +173,7 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
35
     free(link_path);
36
     free(terminal_cmd);
37
     free(script_path);
38
+    free(exe_path);
39
 
40
     /* TODO: unset flag, re-render */
41
 }

b/i3bar/src/xcb.c

46
@@ -901,6 +901,7 @@ void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
47
  */
48
 char *init_xcb_early() {
49
     /* FIXME: xcb_connect leaks Memory */
50
+    size_t path_size = 1024;
51
     xcb_connection = xcb_connect(NULL, &screen);
52
     if (xcb_connection_has_error(xcb_connection)) {
53
         ELOG("Cannot open display\n");
54
@@ -961,18 +962,47 @@ char *init_xcb_early() {
55
     get_atoms();
56
 
57
     xcb_get_property_cookie_t path_cookie;
58
+    xcb_get_property_reply_t *path_reply;
59
+    int len;
60
+    char *path = NULL;
61
+
62
     path_cookie = xcb_get_property_unchecked(xcb_connection,
63
-                                   0,
64
-                                   xcb_root,
65
-                                   atoms[I3_SOCKET_PATH],
66
-                                   XCB_GET_PROPERTY_TYPE_ANY,
67
-                                   0, PATH_MAX);
68
+                                             0,
69
+                                             xcb_root,
70
+                                             atoms[I3_SOCKET_PATH],
71
+                                             XCB_GET_PROPERTY_TYPE_ANY,
72
+                                             0,
73
+                                             (path_size / 4));
74
 
75
     /* We check, if i3 set its socket-path */
76
-    xcb_get_property_reply_t *path_reply = xcb_get_property_reply(xcb_connection,
77
-                                                                  path_cookie,
78
-                                                                  NULL);
79
-    char *path = NULL;
80
+    path_reply = xcb_get_property_reply(xcb_connection,
81
+                                        path_cookie,
82
+                                        NULL);
83
+    if (path_reply) {
84
+        len = xcb_get_property_value_length(path_reply);
85
+    }
86
+
87
+    while (path_reply && len > 0 && len == path_size) {
88
+        // The reply is OK, but we might not have received the end of the path
89
+        // TODO properly use xcb_get_property_value_end instead of doing a new
90
+        // query.
91
+        path_size = path_size * 2;
92
+        path_cookie = xcb_get_property_unchecked(xcb_connection,
93
+                                                 0,
94
+                                                 xcb_root,
95
+                                                 atoms[I3_SOCKET_PATH],
96
+                                                 XCB_GET_PROPERTY_TYPE_ANY,
97
+                                                 0,
98
+                                                 (path_size / 4));
99
+
100
+        path_reply = xcb_get_property_reply(xcb_connection,
101
+                                            path_cookie,
102
+                                            NULL);
103
+        if (path_reply) {
104
+            len = xcb_get_property_value_length(path_reply);
105
+        }
106
+    }
107
+
108
     if (path_reply) {
109
         int len = xcb_get_property_value_length(path_reply);
110
         if (len != 0) {

b/include/libi3.h

115
@@ -369,7 +369,8 @@ char *get_process_filename(const char *prefix);
116
  *
117
  * The implementation follows http://stackoverflow.com/a/933996/712014
118
  *
119
+ * Returned value might (must) be freed by the caller.
120
  */
121
-const char *get_exe_path(const char *argv0);
122
+char *get_exe_path(const char *argv0);
123
 
124
 #endif

b/libi3/get_exe_path.c

129
@@ -3,6 +3,7 @@
130
 #include <stdio.h>
131
 #include <limits.h>
132
 #include <stdlib.h>
133
+#include <errno.h>
134
 
135
 #include "libi3.h"
136
 
137
@@ -11,10 +12,14 @@
138
  *
139
  * The implementation follows http://stackoverflow.com/a/933996/712014
140
  *
141
+ * Returned value might (must) be freed by the caller.
142
  */
143
-const char *get_exe_path(const char *argv0) {
144
-	static char destpath[PATH_MAX];
145
-	char tmp[PATH_MAX];
146
+char *get_exe_path(const char *argv0) {
147
+	size_t destpath_sze = 1024;
148
+	char *destpath = malloc(destpath_sze);
149
+	size_t tmp_sze = 1024;
150
+	char *tmp = malloc(tmp_sze);
151
+
152
 
153
 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
154
 	/* Linux and Debian/kFreeBSD provide /proc/self/exe */
155
@@ -25,30 +30,57 @@ const char *get_exe_path(const char *argv0) {
156
 #endif
157
 	ssize_t linksize;
158
 
159
-	if ((linksize = readlink(exepath, destpath, sizeof(destpath) - 1)) != -1) {
160
+	while ((linksize = readlink(exepath, destpath, destpath_sze)) == destpath_sze) {
161
+		destpath_sze = destpath_sze * 2;
162
+		destpath = realloc(destpath, destpath_sze);
163
+	}
164
+	if (linksize != -1) {
165
 		/* readlink() does not NULL-terminate strings, so we have to. */
166
 		destpath[linksize] = '\0';
167
 
168
+		free(tmp);
169
 		return destpath;
170
 	}
171
 #endif
172
 
173
 	/* argv[0] is most likely a full path if it starts with a slash. */
174
-	if (argv0[0] == '/')
175
-		return argv0;
176
+	if (argv0[0] == '/') {
177
+		destpath_sze = strlen(argv0) + 1;
178
+		destpath = realloc(destpath, destpath_sze);
179
+		strcpy(destpath, argv0);
180
 
181
-	/* if argv[0] contains a /, prepend the working directory */
182
-	if (strchr(argv0, '/') != NULL &&
183
-		getcwd(tmp, sizeof(tmp)) != NULL) {
184
-		snprintf(destpath, sizeof(destpath), "%s/%s", tmp, argv0);
185
+		free(tmp);
186
 		return destpath;
187
 	}
188
 
189
+	/* if argv[0] contains a /, prepend the working directory */
190
+	if (strchr(argv0, '/') != NULL) {
191
+		char *retgcwd;
192
+                while ((retgcwd = getcwd(tmp, tmp_sze)) == NULL && errno == ERANGE) {
193
+			tmp_sze = tmp_sze * 2;
194
+			tmp = realloc(tmp, tmp_sze);
195
+		}
196
+		if (retgcwd != NULL) {
197
+			while(snprintf(destpath, destpath_sze, "%s/%s", tmp, argv0) >= destpath_sze) {
198
+				destpath_sze = destpath_sze * 2;
199
+				destpath = realloc(destpath, destpath_sze);
200
+			}
201
+
202
+			free(tmp);
203
+			return destpath;
204
+		}
205
+	}
206
+
207
 	/* Fall back to searching $PATH (or _CS_PATH in absence of $PATH). */
208
 	char *path = getenv("PATH");
209
 	if (path == NULL) {
210
 		/* _CS_PATH is typically something like "/bin:/usr/bin" */
211
-		confstr(_CS_PATH, tmp, sizeof(tmp));
212
+		int confstrret = confstr(_CS_PATH, tmp, tmp_sze);
213
+		while (confstrret > 0 && confstrret > tmp_sze) {
214
+			tmp_sze = tmp_sze * 2;
215
+			tmp = realloc(tmp, tmp_sze);
216
+			confstrret = confstr(_CS_PATH, tmp, tmp_sze);
217
+		}
218
 		sasprintf(&path, ":%s", tmp);
219
 	} else {
220
 		path = strdup(path);
221
@@ -59,16 +91,23 @@ const char *get_exe_path(const char *argv0) {
222
 		if ((component = strtok(str, ":")) == NULL)
223
 			break;
224
 		str = NULL;
225
-		snprintf(destpath, sizeof(destpath), "%s/%s", component, argv0);
226
+		while(snprintf(destpath, destpath_sze, "%s/%s", component, argv0) >= destpath_sze) {
227
+			destpath_sze = destpath_sze * 2;
228
+			destpath = realloc(destpath, destpath_sze);
229
+		}
230
 		/* Of course this is not 100% equivalent to actually exec()ing the
231
 		 * binary, but meh. */
232
 		if (access(destpath, X_OK) == 0) {
233
 			free(path);
234
+			free(tmp);
235
 			return destpath;
236
 		}
237
 	}
238
-	free(path);
239
 
240
 	/* Last resort: maybe it’s in /usr/bin? */
241
-	return "/usr/bin/i3-nagbar";
242
+	free(destpath);
243
+	free(path);
244
+	free(tmp);
245
+	destpath = strdup("/usr/bin/i3-nagbar");
246
+	return destpath;
247
 }

b/libi3/root_atom_contents.c

252
@@ -29,6 +29,8 @@ char *root_atom_contents(const char *atomname) {
253
     xcb_intern_atom_reply_t *atom_reply;
254
     int screen;
255
     char *content;
256
+    int path_max_sze = 1024; // sould be a multiple of 4
257
+    int prop_value_sze;
258
 
259
     if ((conn = xcb_connect(NULL, &screen)) == NULL ||
260
         xcb_connection_has_error(conn))
261
@@ -46,10 +48,19 @@ char *root_atom_contents(const char *atomname) {
262
     xcb_get_property_cookie_t prop_cookie;
263
     xcb_get_property_reply_t *prop_reply;
264
     prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
265
-                                             XCB_GET_PROPERTY_TYPE_ANY, 0, PATH_MAX);
266
+                                             XCB_GET_PROPERTY_TYPE_ANY, 0, path_max_sze / 4);
267
     prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
268
-    if (prop_reply == NULL || xcb_get_property_value_length(prop_reply) == 0)
269
+    prop_value_sze = xcb_get_property_value_length(prop_reply);
270
+    while (prop_reply != NULL && prop_value_sze < path_max_sze) {
271
+        path_max_sze = path_max_sze * 2;
272
+        prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
273
+                        XCB_GET_PROPERTY_TYPE_ANY, 0, path_max_sze / 4);
274
+        prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
275
+        prop_value_sze = xcb_get_property_value_length(prop_reply);
276
+    }
277
+    if (prop_reply == NULL || prop_value_sze == 0)
278
         return NULL;
279
+
280
     if (prop_reply->type == XCB_ATOM_CARDINAL) {
281
         /* We treat a CARDINAL as a >= 32-bit unsigned int. The only CARDINAL
282
          * we query is I3_PID, which is 32-bit. */

b/src/display_version.c

287
@@ -128,13 +128,25 @@ void display_running_version(void) {
288
     printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom);
289
 
290
 #ifdef __linux__
291
-    char exepath[PATH_MAX],
292
-         destpath[PATH_MAX];
293
+    char *exepath,
294
+         *destpath;
295
+    size_t exepath_sze = 1024,
296
+           destpath_sze = 1024;
297
     ssize_t linksize;
298
 
299
-    snprintf(exepath, sizeof(exepath), "/proc/%d/exe", getpid());
300
+    exepath = malloc(exepath_sze);
301
+    destpath = malloc(destpath_sze);
302
 
303
-    if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1)
304
+    while (snprintf(exepath, exepath_sze, "/proc/%d/exe", getpid()) >= exepath_sze) {
305
+            exepath_sze = exepath_sze * 2;
306
+            exepath = realloc(exepath, exepath_sze);
307
+    }
308
+
309
+    while ((linksize = readlink(exepath, destpath, destpath_sze)) == destpath_sze) {
310
+            destpath_sze = destpath_sze * 2;
311
+            destpath = realloc(destpath, destpath_sze);
312
+    }
313
+    if (linksize == -1)
314
         err(EXIT_FAILURE, "readlink(%s)", exepath);
315
 
316
     /* readlink() does not NULL-terminate strings, so we have to. */
317
@@ -143,9 +155,16 @@ void display_running_version(void) {
318
     printf("\n");
319
     printf("The i3 binary you just called: %s\n", destpath);
320
 
321
-    snprintf(exepath, sizeof(exepath), "/proc/%s/exe", pid_from_atom);
322
+    while (snprintf(exepath, exepath_sze, "/proc/%s/exe", pid_from_atom) >= exepath_sze) {
323
+            exepath_sze = exepath_sze * 2;
324
+            exepath = realloc(exepath, exepath_sze);
325
+    }
326
 
327
-    if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1)
328
+    while ((linksize = readlink(exepath, destpath, destpath_sze)) == destpath_sze) {
329
+        destpath_sze = destpath_sze * 2;
330
+        destpath = realloc(destpath, destpath_sze);
331
+    }
332
+    if (linksize == -1)
333
         err(EXIT_FAILURE, "readlink(%s)", exepath);
334
 
335
     /* readlink() does not NULL-terminate strings, so we have to. */
336
@@ -159,7 +178,10 @@ void display_running_version(void) {
337
     /* Since readlink() might put a "(deleted)" somewhere in the buffer and
338
      * stripping that out seems hackish and ugly, we read the process’s argv[0]
339
      * instead. */
340
-    snprintf(exepath, sizeof(exepath), "/proc/%s/cmdline", pid_from_atom);
341
+    while (snprintf(exepath, exepath_sze, "/proc/%s/cmdline", pid_from_atom) >= exepath_sze) {
342
+        exepath_sze = exepath_sze * 2;
343
+        exepath = realloc(exepath, exepath_sze);
344
+    }
345
 
346
     int fd;
347
     if ((fd = open(exepath, O_RDONLY)) == -1)
348
@@ -169,6 +191,9 @@ void display_running_version(void) {
349
     close(fd);
350
 
351
     printf("The i3 binary you are running: %s\n", destpath);
352
+
353
+    free(exepath);
354
+    free(destpath);
355
 #endif
356
 
357
     yajl_free(handle);

b/src/main.c

362
@@ -488,14 +488,22 @@ int main(int argc, char *argv[]) {
363
 
364
         /* The following code is helpful, but not required. We thus don’t pay
365
          * much attention to error handling, non-linux or other edge cases. */
366
-        char cwd[PATH_MAX];
367
         LOG("CORE DUMPS: You are running a development version of i3, so coredumps were automatically enabled (ulimit -c unlimited).\n");
368
-        if (getcwd(cwd, sizeof(cwd)) != NULL)
369
+        char *cwd = 0;
370
+        size_t cwd_sze = 1024;
371
+        cwd = malloc(cwd_sze);
372
+        while (getcwd(cwd, cwd_sze) == NULL && errno == ERANGE) {
373
+                cwd_sze = cwd_sze * 2;
374
+                cwd = realloc(cwd, cwd_sze);
375
+        }
376
+        if (cwd != NULL)
377
             LOG("CORE DUMPS: Your current working directory is \"%s\".\n", cwd);
378
+        free(cwd);
379
+
380
         int patternfd;
381
         if ((patternfd = open("/proc/sys/kernel/core_pattern", O_RDONLY)) >= 0) {
382
-            memset(cwd, '\0', sizeof(cwd));
383
-            if (read(patternfd, cwd, sizeof(cwd)) > 0)
384
+            memset(cwd, '\0', sizeof(cwd_sze));
385
+            if (read(patternfd, cwd, sizeof(cwd_sze)) > 0)
386
                 /* a trailing newline is included in cwd */
387
                 LOG("CORE DUMPS: Your core_pattern is: %s", cwd);
388
             close(patternfd);