314a0e98cc71c3a8948b551381c2b1c7daa0fa9b
1 /*
2 * $Id$
3 *
4 * (c) 2004 by Kalle Wallin <kaw@linux.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <glib.h>
26 #include "config.h"
27 #include "ncmpc.h"
28 #include "support.h"
29 #include "options.h"
31 #define MAX_LONGOPT_LENGTH 32
33 #define ERROR_UNKNOWN_OPTION 0x01
34 #define ERROR_BAD_ARGUMENT 0x02
35 #define ERROR_GOT_ARGUMENT 0x03
36 #define ERROR_MISSING_ARGUMENT 0x04
40 typedef struct
41 {
42 int shortopt;
43 char *longopt;
44 char *argument;
45 char *descrition;
46 } arg_opt_t;
49 typedef void (*option_callback_fn_t) (int c, char *arg);
52 options_t options;
54 static arg_opt_t option_table[] = {
55 { '?', "help", NULL, "Show this help message" },
56 { 'V', "version", NULL, "Display version information" },
57 { 'c', "colors", NULL, "Enable colors" },
58 { 'C', "no-colors", NULL, "Disable colors" },
59 { 'e', "exit", NULL, "Exit on connection errors" },
60 { 'p', "port", "PORT", "Connect to server on port [" DEFAULT_PORT_STR "]" },
61 { 'h', "host", "HOST", "Connect to server on host [" DEFAULT_HOST "]" },
62 { 'P', "password","PASSWORD", "Connect with password" },
63 { 'f', "config", "FILE", "Read configuration from file" },
64 { 'k', "key-file","FILE", "Read configuration from file" },
65 #ifdef DEBUG
66 { 'D', "debug", NULL, "Enable debug output on stderr" },
67 #endif
68 { 0, NULL, NULL, NULL },
69 };
71 static arg_opt_t *
72 lookup_option(int s, char *l)
73 {
74 int i;
76 i=0;
77 while( option_table[i].descrition )
78 {
79 if( l && strcmp(l, option_table[i].longopt) == 0 )
80 return &option_table[i];;
81 if( s && s==option_table[i].shortopt )
82 return &option_table[i];;
83 i++;
84 }
85 return NULL;
86 }
88 static void
89 option_error(int error, char *option, char *arg)
90 {
91 switch(error)
92 {
93 case ERROR_UNKNOWN_OPTION:
94 fprintf(stderr, PACKAGE ": invalid option %s\n", option);
95 break;
96 case ERROR_BAD_ARGUMENT:
97 fprintf(stderr, PACKAGE ": bad argument: %s\n", option);
98 break;
99 case ERROR_GOT_ARGUMENT:
100 fprintf(stderr, PACKAGE ": invalid option %s=%s\n", option, arg);
101 break;
102 case ERROR_MISSING_ARGUMENT:
103 fprintf(stderr, PACKAGE ": missing value for %s\n", option);
104 break;
105 default:
106 fprintf(stderr, PACKAGE ": internal error %d\n", error);
107 break;
108 }
109 exit(EXIT_FAILURE);
110 }
112 static void
113 display_help(void)
114 {
115 int i = 0;
117 printf("Usage: %s [OPTION]...\n", PACKAGE);
118 while( option_table[i].descrition )
119 {
120 char tmp[MAX_LONGOPT_LENGTH];
122 if( option_table[i].argument )
123 g_snprintf(tmp, MAX_LONGOPT_LENGTH, "%s=%s",
124 option_table[i].longopt,
125 option_table[i].argument);
126 else
127 g_strlcpy(tmp, option_table[i].longopt, 64);
129 printf(" -%c, --%-20s %s\n",
130 option_table[i].shortopt,
131 tmp,
132 option_table[i].descrition);
133 i++;
134 }
135 }
137 static void
138 handle_option(int c, char *arg)
139 {
140 D("option callback -%c %s\n", c, arg);
141 switch(c)
142 {
143 case '?': /* --help */
144 display_help();
145 exit(EXIT_SUCCESS);
146 case 'V': /* --version */
147 printf("Version %s\n", VERSION);
148 exit(EXIT_SUCCESS);
149 case 'c': /* --colors */
150 options.enable_colors = TRUE;
151 break;
152 case 'C': /* --no-colors */
153 options.enable_colors = FALSE;
154 break;
155 case 'e': /* --exit */
156 options.reconnect = FALSE;
157 break;
158 case 'p': /* --port */
159 options.port = atoi(arg);
160 break;
161 case 'h': /* --host */
162 if( options.host )
163 g_free(options.host);
164 options.host = g_strdup(arg);
165 break;
166 case 'P': /* --password */
167 if( options.password )
168 g_free(options.password);
169 options.password = locale_to_utf8(arg);
170 break;
171 case 'f': /* --config */
172 if( options.config_file )
173 g_free(options.config_file);
174 options.config_file = g_strdup(arg);
175 break;
176 case 'k': /* --key-file */
177 if( options.key_file )
178 g_free(options.key_file);
179 options.key_file = g_strdup(arg);
180 break;
181 case 'D': /* --debug */
182 options.debug = TRUE;
183 break;
184 default:
185 fprintf(stderr,"Unknown Option %c = %s\n", c, arg);
186 break;
187 }
188 }
190 options_t *
191 options_get(void)
192 {
193 return &options;
194 }
196 options_t *
197 options_parse(int argc, const char *argv[])
198 {
199 int i;
200 arg_opt_t *opt = NULL;
201 option_callback_fn_t option_cb = handle_option;
203 i=1;
204 while( i<argc )
205 {
206 char *arg = (char *) argv[i];
207 size_t len=strlen(arg);
209 /* check for a long option */
210 if( g_str_has_prefix(arg, "--") )
211 {
212 char *name, *value;
214 /* make shure we got an argument for the previous option */
215 if( opt && opt->argument )
216 option_error(ERROR_MISSING_ARGUMENT, opt->longopt, opt->argument);
218 /* retreive a option argument */
219 if( (value=g_strrstr(arg+2, "=")) )
220 {
221 *value = '\0';
222 name = g_strdup(arg);
223 *value = '=';
224 value++;
225 }
226 else
227 name = g_strdup(arg);
229 /* check if the option exists */
230 if( (opt=lookup_option(0, name+2)) == NULL )
231 option_error(ERROR_UNKNOWN_OPTION, name, NULL);
232 g_free(name);
234 /* abort if we got an argument to the option and dont want one */
235 if( value && opt->argument==NULL )
236 option_error(ERROR_GOT_ARGUMENT, arg, value);
238 /* execute option callback */
239 if( value || opt->argument==NULL )
240 {
241 option_cb (opt->shortopt, value);
242 opt = NULL;
243 }
244 }
245 /* check for a short option */
246 else if( len==2 && g_str_has_prefix(arg, "-") )
247 {
248 /* make shure we got an argument for the previous option */
249 if( opt && opt->argument )
250 option_error(ERROR_MISSING_ARGUMENT, opt->longopt, opt->argument);
252 /* check if the option exists */
253 if( (opt=lookup_option(arg[1], NULL))==NULL )
254 option_error(ERROR_UNKNOWN_OPTION, arg, NULL);
256 /* if no option argument is needed execute callback */
257 if( opt->argument==NULL )
258 {
259 option_cb (opt->shortopt, NULL);
260 opt = NULL;
261 }
262 }
263 else
264 {
265 /* is this a option argument? */
266 if( opt && opt->argument)
267 {
268 option_cb (opt->shortopt, arg);
269 opt = NULL;
270 }
271 else
272 option_error(ERROR_BAD_ARGUMENT, arg, NULL);
273 }
274 i++;
275 }
277 if( opt && opt->argument==NULL)
278 option_cb (opt->shortopt, NULL);
279 else if( opt && opt->argument )
280 option_error(ERROR_MISSING_ARGUMENT, opt->longopt, opt->argument);
282 return &options;
283 }
286 options_t *
287 options_init( void )
288 {
289 const char *value;
290 char *tmp;
292 memset(&options, 0, sizeof(options_t));
294 /* get initial values for host and password from MPD_HOST (enviroment) */
295 if( (value=g_getenv(MPD_HOST_ENV)) )
296 options.host = g_strdup(value);
297 else
298 options.host = g_strdup(DEFAULT_HOST);
299 if( (tmp=g_strstr_len(options.host, strlen(options.host), "@")) )
300 {
301 char *oldhost = options.host;
302 *tmp = '\0';
303 options.password = locale_to_utf8(oldhost);
304 options.host = g_strdup(tmp+1);
305 g_free(oldhost);
306 }
307 /* get initial values for port from MPD_PORT (enviroment) */
308 if( (value=g_getenv(MPD_PORT_ENV)) )
309 options.port = atoi(value);
310 else
311 options.port = DEFAULT_PORT;
313 /* default option values */
314 options.reconnect = TRUE;
315 options.find_wrap = TRUE;
316 options.wide_cursor = TRUE;
317 options.audible_bell = TRUE;
319 return &options;
320 }