mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Instead of eating 1024 bytes or so for the arguments of each command,
save memory by using an RB tree. From Tiago Cunha.
This commit is contained in:
		
							
								
								
									
										108
									
								
								arguments.c
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								arguments.c
									
									
									
									
									
								
							| @@ -18,12 +18,26 @@ | |||||||
|  |  | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
|  |  | ||||||
| #include <bitstring.h> |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #include "tmux.h" | #include "tmux.h" | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Manipulate command arguments. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | struct args_entry	*args_find(struct args *, u_char); | ||||||
|  |  | ||||||
|  | RB_GENERATE(args_tree, args_entry, entry, args_cmp); | ||||||
|  |  | ||||||
|  | /* Arguments tree comparison function. */ | ||||||
|  | int | ||||||
|  | args_cmp(struct args_entry *a1, struct args_entry *a2) | ||||||
|  | { | ||||||
|  | 	return (a1->flag - a2->flag); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Create an arguments set with no flags. */ | /* Create an arguments set with no flags. */ | ||||||
| struct args * | struct args * | ||||||
| args_create(int argc, ...) | args_create(int argc, ...) | ||||||
| @@ -33,8 +47,6 @@ args_create(int argc, ...) | |||||||
| 	int		 i; | 	int		 i; | ||||||
|  |  | ||||||
| 	args = xcalloc(1, sizeof *args); | 	args = xcalloc(1, sizeof *args); | ||||||
| 	if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) |  | ||||||
| 		fatal("bit_alloc failed"); |  | ||||||
|  |  | ||||||
| 	args->argc = argc; | 	args->argc = argc; | ||||||
| 	if (argc == 0) | 	if (argc == 0) | ||||||
| @@ -50,6 +62,16 @@ args_create(int argc, ...) | |||||||
| 	return (args); | 	return (args); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Find a flag in the arguments tree. */ | ||||||
|  | struct args_entry * | ||||||
|  | args_find(struct args *args, u_char ch) | ||||||
|  | { | ||||||
|  | 	struct args_entry	entry; | ||||||
|  |  | ||||||
|  | 	entry.flag = ch; | ||||||
|  | 	return (RB_FIND(args_tree, &args->tree, &entry)); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Parse an argv and argc into a new argument set. */ | /* Parse an argv and argc into a new argument set. */ | ||||||
| struct args * | struct args * | ||||||
| args_parse(const char *template, int argc, char **argv) | args_parse(const char *template, int argc, char **argv) | ||||||
| @@ -59,26 +81,18 @@ args_parse(const char *template, int argc, char **argv) | |||||||
| 	int		 opt; | 	int		 opt; | ||||||
|  |  | ||||||
| 	args = xcalloc(1, sizeof *args); | 	args = xcalloc(1, sizeof *args); | ||||||
| 	if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) |  | ||||||
| 		fatal("bit_alloc failed"); |  | ||||||
|  |  | ||||||
| 	optreset = 1; | 	optreset = 1; | ||||||
| 	optind = 1; | 	optind = 1; | ||||||
|  |  | ||||||
| 	while ((opt = getopt(argc, argv, template)) != -1) { | 	while ((opt = getopt(argc, argv, template)) != -1) { | ||||||
| 		if (opt < 0 || opt >= SCHAR_MAX) | 		if (opt < 0) | ||||||
| 			continue; | 			continue; | ||||||
| 		if (opt == '?' || (ptr = strchr(template, opt)) == NULL) { | 		if (opt == '?' || (ptr = strchr(template, opt)) == NULL) { | ||||||
| 			free(args->flags); | 			args_free(args); | ||||||
| 			free(args); |  | ||||||
| 			return (NULL); | 			return (NULL); | ||||||
| 		} | 		} | ||||||
|  | 		args_set(args, opt, optarg); | ||||||
| 		bit_set(args->flags, opt); |  | ||||||
| 		if (ptr[1] == ':') { |  | ||||||
| 			free(args->values[opt]); |  | ||||||
| 			args->values[opt] = xstrdup(optarg); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	argc -= optind; | 	argc -= optind; | ||||||
| 	argv += optind; | 	argv += optind; | ||||||
| @@ -93,14 +107,17 @@ args_parse(const char *template, int argc, char **argv) | |||||||
| void | void | ||||||
| args_free(struct args *args) | args_free(struct args *args) | ||||||
| { | { | ||||||
| 	u_int	i; | 	struct args_entry	*entry; | ||||||
|  | 	struct args_entry	*entry1; | ||||||
|  |  | ||||||
| 	cmd_free_argv(args->argc, args->argv); | 	cmd_free_argv(args->argc, args->argv); | ||||||
|  |  | ||||||
| 	for (i = 0; i < SCHAR_MAX; i++) | 	RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { | ||||||
| 		free(args->values[i]); | 		RB_REMOVE(args_tree, &args->tree, entry); | ||||||
|  | 		free(entry->value); | ||||||
|  | 		free(entry); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	free(args->flags); |  | ||||||
| 	free(args); | 	free(args); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -111,6 +128,7 @@ args_print(struct args *args, char *buf, size_t len) | |||||||
| 	size_t		 	 off; | 	size_t		 	 off; | ||||||
| 	int			 i; | 	int			 i; | ||||||
| 	const char		*quotes; | 	const char		*quotes; | ||||||
|  | 	struct args_entry	*entry; | ||||||
|  |  | ||||||
| 	/* There must be at least one byte at the start. */ | 	/* There must be at least one byte at the start. */ | ||||||
| 	if (len == 0) | 	if (len == 0) | ||||||
| @@ -119,23 +137,23 @@ args_print(struct args *args, char *buf, size_t len) | |||||||
|  |  | ||||||
| 	/* Process the flags first. */ | 	/* Process the flags first. */ | ||||||
| 	buf[off++] = '-'; | 	buf[off++] = '-'; | ||||||
| 	for (i = 0; i < SCHAR_MAX; i++) { | 	RB_FOREACH(entry, args_tree, &args->tree) { | ||||||
| 		if (!bit_test(args->flags, i) || args->values[i] != NULL) | 		if (entry->value != NULL) | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
| 		if (off == len - 1) { | 		if (off == len - 1) { | ||||||
| 			buf[off] = '\0'; | 			buf[off] = '\0'; | ||||||
| 			return (len); | 			return (len); | ||||||
| 		} | 		} | ||||||
| 		buf[off++] = i; | 		buf[off++] = entry->flag; | ||||||
| 		buf[off] = '\0'; | 		buf[off] = '\0'; | ||||||
| 	} | 	} | ||||||
| 	if (off == 1) | 	if (off == 1) | ||||||
| 		buf[--off] = '\0'; | 		buf[--off] = '\0'; | ||||||
|  |  | ||||||
| 	/* Then the flags with arguments. */ | 	/* Then the flags with arguments. */ | ||||||
| 	for (i = 0; i < SCHAR_MAX; i++) { | 	RB_FOREACH(entry, args_tree, &args->tree) { | ||||||
| 		if (!bit_test(args->flags, i) || args->values[i] == NULL) | 		if (entry->value == NULL) | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
| 		if (off >= len) { | 		if (off >= len) { | ||||||
| @@ -143,12 +161,13 @@ args_print(struct args *args, char *buf, size_t len) | |||||||
| 			return (len); | 			return (len); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (strchr(args->values[i], ' ') != NULL) | 		if (strchr(entry->value, ' ') != NULL) | ||||||
| 			quotes = "\""; | 			quotes = "\""; | ||||||
| 		else | 		else | ||||||
| 			quotes = ""; | 			quotes = ""; | ||||||
| 		off += xsnprintf(buf + off, len - off, "%s-%c %s%s%s", | 		off += xsnprintf(buf + off, len - off, "%s-%c %s%s%s", | ||||||
| 		    off != 0 ? " " : "", i, quotes, args->values[i], quotes); | 		    off != 0 ? " " : "", entry->flag, quotes, entry->value, | ||||||
|  | 		    quotes); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* And finally the argument vector. */ | 	/* And finally the argument vector. */ | ||||||
| @@ -173,42 +192,59 @@ args_print(struct args *args, char *buf, size_t len) | |||||||
| int | int | ||||||
| args_has(struct args *args, u_char ch) | args_has(struct args *args, u_char ch) | ||||||
| { | { | ||||||
| 	return (bit_test(args->flags, ch)); | 	return (args_find(args, ch) == NULL ? 0 : 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Set argument value. */ | /* Set argument value in the arguments tree. */ | ||||||
| void | void | ||||||
| args_set(struct args *args, u_char ch, const char *value) | args_set(struct args *args, u_char ch, const char *value) | ||||||
| { | { | ||||||
| 	free(args->values[ch]); | 	struct args_entry	*entry; | ||||||
|  |  | ||||||
|  | 	/* Replace existing argument. */ | ||||||
|  | 	if ((entry = args_find(args, ch)) != NULL) { | ||||||
|  | 		free(entry->value); | ||||||
| 		if (value != NULL) | 		if (value != NULL) | ||||||
| 		args->values[ch] = xstrdup(value); | 			entry->value = xstrdup(value); | ||||||
| 		else | 		else | ||||||
| 		args->values[ch] = NULL; | 			entry->value = NULL; | ||||||
| 	bit_set(args->flags, ch); | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	entry = xcalloc(1, sizeof *entry); | ||||||
|  | 	entry->flag = ch; | ||||||
|  | 	if (value != NULL) | ||||||
|  | 		entry->value = xstrdup(value); | ||||||
|  |  | ||||||
|  | 	RB_INSERT(args_tree, &args->tree, entry); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Get argument value. Will be NULL if it isn't present. */ | /* Get argument value. Will be NULL if it isn't present. */ | ||||||
| const char * | const char * | ||||||
| args_get(struct args *args, u_char ch) | args_get(struct args *args, u_char ch) | ||||||
| { | { | ||||||
| 	return (args->values[ch]); | 	struct args_entry	*entry; | ||||||
|  |  | ||||||
|  | 	if ((entry = args_find(args, ch)) == NULL) | ||||||
|  | 		return (NULL); | ||||||
|  | 	return (entry->value); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Convert an argument value to a number. */ | /* Convert an argument value to a number. */ | ||||||
| long long | long long | ||||||
| args_strtonum(struct args *args, | args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, | ||||||
|     u_char ch, long long minval, long long maxval, char **cause) |     char **cause) | ||||||
| { | { | ||||||
| 	const char		*errstr; | 	const char		*errstr; | ||||||
| 	long long 	 	 ll; | 	long long 	 	 ll; | ||||||
|  | 	struct args_entry	*entry; | ||||||
|  |  | ||||||
| 	if (!args_has(args, ch)) { | 	if ((entry = args_find(args, ch)) == NULL) { | ||||||
| 		*cause = xstrdup("missing"); | 		*cause = xstrdup("missing"); | ||||||
| 		return (0); | 		return (0); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ll = strtonum(args->values[ch], minval, maxval, &errstr); | 	ll = strtonum(entry->value, minval, maxval, &errstr); | ||||||
| 	if (errstr != NULL) { | 	if (errstr != NULL) { | ||||||
| 		*cause = xstrdup(errstr); | 		*cause = xstrdup(errstr); | ||||||
| 		return (0); | 		return (0); | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -1361,11 +1361,16 @@ struct client { | |||||||
| }; | }; | ||||||
| ARRAY_DECL(clients, struct client *); | ARRAY_DECL(clients, struct client *); | ||||||
|  |  | ||||||
| /* Parsed arguments. */ | /* Parsed arguments structures. */ | ||||||
| struct args { | struct args_entry { | ||||||
| 	bitstr_t	*flags; | 	u_char			 flag; | ||||||
| 	char		*values[SCHAR_MAX]; /* XXX This is awfully big. */ | 	char			*value; | ||||||
|  | 	RB_ENTRY(args_entry)	 entry; | ||||||
|  | }; | ||||||
|  | RB_HEAD(args_tree, args_entry); | ||||||
|  |  | ||||||
|  | struct args { | ||||||
|  | 	struct args_tree	  tree; | ||||||
| 	int		 	  argc; | 	int		 	  argc; | ||||||
| 	char	       		**argv; | 	char	       		**argv; | ||||||
| }; | }; | ||||||
| @@ -1724,6 +1729,8 @@ extern const char clock_table[14][5][5]; | |||||||
| void		 clock_draw(struct screen_write_ctx *, int, int); | void		 clock_draw(struct screen_write_ctx *, int, int); | ||||||
|  |  | ||||||
| /* arguments.c */ | /* arguments.c */ | ||||||
|  | int		 args_cmp(struct args_entry *, struct args_entry *); | ||||||
|  | RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); | ||||||
| struct args	*args_create(int, ...); | struct args	*args_create(int, ...); | ||||||
| struct args	*args_parse(const char *, int, char **); | struct args	*args_parse(const char *, int, char **); | ||||||
| void		 args_free(struct args *); | void		 args_free(struct args *); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Nicholas Marriott
					Nicholas Marriott