xref: /openbsd-src/usr.bin/tmux/cfg.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /* $OpenBSD: cfg.c,v 1.16 2012/07/11 07:10:15 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "tmux.h"
28 
29 /*
30  * Config file parser. Pretty quick and simple, each line is parsed into a
31  * argv array and executed as a command.
32  */
33 
34 void printflike2 cfg_print(struct cmd_ctx *, const char *, ...);
35 void printflike2 cfg_error(struct cmd_ctx *, const char *, ...);
36 
37 char	 	       *cfg_cause;
38 int     	 	cfg_finished;
39 struct causelist	cfg_causes = ARRAY_INITIALIZER;
40 
41 /* ARGSUSED */
42 void printflike2
43 cfg_print(unused struct cmd_ctx *ctx, unused const char *fmt, ...)
44 {
45 }
46 
47 /* ARGSUSED */
48 void printflike2
49 cfg_error(unused struct cmd_ctx *ctx, const char *fmt, ...)
50 {
51 	va_list	ap;
52 
53 	va_start(ap, fmt);
54 	xvasprintf(&cfg_cause, fmt, ap);
55 	va_end(ap);
56 }
57 
58 void printflike2
59 cfg_add_cause(struct causelist *causes, const char *fmt, ...)
60 {
61 	char	*cause;
62 	va_list	 ap;
63 
64 	va_start(ap, fmt);
65 	xvasprintf(&cause, fmt, ap);
66 	va_end(ap);
67 
68 	ARRAY_ADD(causes, cause);
69 }
70 
71 /*
72  * Load configuration file. Returns -1 for an error with a list of messages in
73  * causes. Note that causes must be initialised by the caller!
74  */
75 int
76 load_cfg(const char *path, struct cmd_ctx *ctxin, struct causelist *causes)
77 {
78 	FILE		*f;
79 	u_int		 n;
80 	char		*buf, *line, *cause;
81 	size_t		 len;
82 	struct cmd_list	*cmdlist;
83 	struct cmd_ctx	 ctx;
84 	enum cmd_retval	 retval;
85 
86 	if ((f = fopen(path, "rb")) == NULL) {
87 		cfg_add_cause(causes, "%s: %s", path, strerror(errno));
88 		return (-1);
89 	}
90 	n = 0;
91 
92 	line = NULL;
93 	retval = CMD_RETURN_NORMAL;
94 	while ((buf = fgetln(f, &len))) {
95 		if (buf[len - 1] == '\n')
96 			len--;
97 
98 		if (line != NULL)
99 			line = xrealloc(line, 1, strlen(line) + len + 1);
100 		else {
101 			line = xmalloc(len + 1);
102 			*line = '\0';
103 		}
104 
105 		/* Append buffer to line. strncat will terminate. */
106 		strncat(line, buf, len);
107 		n++;
108 
109 		/* Continuation: get next line? */
110 		len = strlen(line);
111 		if (len > 0 && line[len - 1] == '\\') {
112 			line[len - 1] = '\0';
113 			/* Ignore escaped backslash at EOL. */
114 			if (len > 1 && line[len - 2] != '\\')
115 				continue;
116 		}
117 		buf = line;
118 		line = NULL;
119 
120 		if (cmd_string_parse(buf, &cmdlist, &cause) != 0) {
121 			free(buf);
122 			if (cause == NULL)
123 				continue;
124 			cfg_add_cause(causes, "%s: %u: %s", path, n, cause);
125 			free(cause);
126 			continue;
127 		} else
128 			free(buf);
129 		if (cmdlist == NULL)
130 			continue;
131 		cfg_cause = NULL;
132 
133 		if (ctxin == NULL) {
134 			ctx.msgdata = NULL;
135 			ctx.curclient = NULL;
136 			ctx.cmdclient = NULL;
137 		} else {
138 			ctx.msgdata = ctxin->msgdata;
139 			ctx.curclient = ctxin->curclient;
140 			ctx.cmdclient = ctxin->cmdclient;
141 		}
142 
143 		ctx.error = cfg_error;
144 		ctx.print = cfg_print;
145 		ctx.info = cfg_print;
146 
147 		cfg_cause = NULL;
148 		switch (cmd_list_exec(cmdlist, &ctx)) {
149 		case CMD_RETURN_YIELD:
150 			if (retval != CMD_RETURN_ATTACH)
151 				retval = CMD_RETURN_YIELD;
152 			break;
153 		case CMD_RETURN_ATTACH:
154 			retval = CMD_RETURN_ATTACH;
155 			break;
156 		case CMD_RETURN_ERROR:
157 		case CMD_RETURN_NORMAL:
158 			break;
159 		}
160 		cmd_list_free(cmdlist);
161 		if (cfg_cause != NULL) {
162 			cfg_add_cause(
163 			    causes, "%s: %d: %s", path, n, cfg_cause);
164 			free(cfg_cause);
165 		}
166 	}
167 	if (line != NULL) {
168 		cfg_add_cause(causes,
169 		    "%s: %d: line continuation at end of file", path, n);
170 		free(line);
171 	}
172 	fclose(f);
173 
174 	return (retval);
175 }
176