umbctl.c revision 1.3 1 1.3 khorben /* $NetBSD: umbctl.c,v 1.3 2020/03/22 07:45:02 khorben Exp $ */
2 1.1 khorben /*
3 1.1 khorben * Copyright (c) 2018 Pierre Pronchery <khorben (at) defora.org>
4 1.1 khorben *
5 1.1 khorben * All rights reserved.
6 1.1 khorben *
7 1.1 khorben * Redistribution and use in source and binary forms, with or without
8 1.1 khorben * modification, are permitted provided that the following conditions
9 1.1 khorben * are met:
10 1.1 khorben * 1. Redistributions of source code must retain the above copyright
11 1.1 khorben * notice, this list of conditions and the following disclaimer.
12 1.1 khorben * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 khorben * notice, this list of conditions and the following disclaimer in the
14 1.1 khorben * documentation and/or other materials provided with the distribution.
15 1.1 khorben *
16 1.1 khorben * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
17 1.1 khorben * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 1.1 khorben * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 1.1 khorben * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 1.1 khorben * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 1.1 khorben * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 1.1 khorben * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 1.1 khorben * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 1.1 khorben * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 1.1 khorben * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 1.1 khorben */
27 1.1 khorben
28 1.1 khorben
29 1.1 khorben
30 1.1 khorben #include <sys/endian.h>
31 1.1 khorben #include <sys/ioctl.h>
32 1.1 khorben #include <sys/socket.h>
33 1.1 khorben
34 1.1 khorben #include <net/if.h>
35 1.1 khorben
36 1.1 khorben #include <ctype.h>
37 1.1 khorben #include <errno.h>
38 1.1 khorben #include <stdarg.h>
39 1.1 khorben #include <stdio.h>
40 1.1 khorben #include <string.h>
41 1.1 khorben #include <unistd.h>
42 1.1 khorben
43 1.1 khorben #include <dev/usb/mbim.h>
44 1.1 khorben #include <dev/usb/if_umbreg.h>
45 1.1 khorben
46 1.1 khorben
47 1.1 khorben /* constants */
48 1.1 khorben static const struct umb_valdescr _umb_regstate[] =
49 1.1 khorben MBIM_REGSTATE_DESCRIPTIONS;
50 1.1 khorben
51 1.1 khorben static const struct umb_valdescr _umb_dataclass[] =
52 1.1 khorben MBIM_DATACLASS_DESCRIPTIONS;
53 1.1 khorben
54 1.1 khorben static const struct umb_valdescr _umb_state[] =
55 1.1 khorben UMB_INTERNAL_STATE_DESCRIPTIONS;
56 1.1 khorben
57 1.1 khorben static const struct umb_valdescr _umb_regmode[] =
58 1.1 khorben {
59 1.1 khorben { MBIM_REGMODE_UNKNOWN, "unknown" },
60 1.1 khorben { MBIM_REGMODE_AUTOMATIC, "automatic" },
61 1.1 khorben { MBIM_REGMODE_MANUAL, "manual" },
62 1.1 khorben { 0, NULL }
63 1.1 khorben };
64 1.1 khorben
65 1.1 khorben static const struct umb_valdescr _umb_ber[] =
66 1.1 khorben {
67 1.1 khorben { UMB_BER_EXCELLENT, "excellent" },
68 1.1 khorben { UMB_BER_VERYGOOD, "very good" },
69 1.1 khorben { UMB_BER_GOOD, "good" },
70 1.1 khorben { UMB_BER_OK, "ok" },
71 1.1 khorben { UMB_BER_MEDIUM, "medium" },
72 1.1 khorben { UMB_BER_BAD, "bad" },
73 1.1 khorben { UMB_BER_VERYBAD, "very bad" },
74 1.1 khorben { UMB_BER_EXTREMELYBAD, "extremely bad" },
75 1.1 khorben { 0, NULL }
76 1.1 khorben };
77 1.1 khorben
78 1.1 khorben
79 1.1 khorben /* prototypes */
80 1.1 khorben static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen);
81 1.1 khorben static int _error(int ret, char const * format, ...);
82 1.1 khorben static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]);
83 1.3 khorben static int _umbctl_file(char const * ifname, char const * filename,
84 1.3 khorben int verbose);
85 1.1 khorben static void _umbctl_info(char const * ifname, struct umb_info * umbi);
86 1.1 khorben static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request,
87 1.1 khorben struct ifreq * ifr);
88 1.1 khorben static int _umbctl_set(char const * ifname, struct umb_parameter * umbp,
89 1.1 khorben int argc, char * argv[]);
90 1.1 khorben static int _umbctl_socket(void);
91 1.1 khorben static int _usage(void);
92 1.1 khorben static void _utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen);
93 1.1 khorben
94 1.1 khorben
95 1.1 khorben /* functions */
96 1.1 khorben /* char_to_utf16 */
97 1.1 khorben /* this function is from OpenBSD's ifconfig(8) */
98 1.1 khorben static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen)
99 1.1 khorben {
100 1.1 khorben int n = 0;
101 1.1 khorben uint16_t c;
102 1.1 khorben
103 1.1 khorben for (;;) {
104 1.1 khorben c = *in++;
105 1.1 khorben
106 1.1 khorben if (c == '\0') {
107 1.1 khorben /*
108 1.1 khorben * NUL termination is not required, but zero out the
109 1.1 khorben * residual buffer
110 1.1 khorben */
111 1.1 khorben memset(out, 0, outlen);
112 1.1 khorben return n;
113 1.1 khorben }
114 1.1 khorben if (outlen < sizeof(*out))
115 1.1 khorben return -1;
116 1.1 khorben
117 1.1 khorben *out++ = htole16(c);
118 1.1 khorben n += sizeof(*out);
119 1.1 khorben outlen -= sizeof(*out);
120 1.1 khorben }
121 1.1 khorben }
122 1.1 khorben
123 1.1 khorben
124 1.1 khorben /* error */
125 1.2 roy __printflike(2, 3) static int _error(int ret, char const * format, ...)
126 1.1 khorben {
127 1.1 khorben va_list ap;
128 1.1 khorben
129 1.1 khorben fputs("umbctl: ", stderr);
130 1.1 khorben va_start(ap, format);
131 1.1 khorben vfprintf(stderr, format, ap);
132 1.1 khorben va_end(ap);
133 1.1 khorben fputs("\n", stderr);
134 1.1 khorben return ret;
135 1.1 khorben }
136 1.1 khorben
137 1.1 khorben
138 1.1 khorben /* umbctl */
139 1.1 khorben static int _umbctl(char const * ifname, int verbose, int argc, char * argv[])
140 1.1 khorben {
141 1.1 khorben int fd;
142 1.1 khorben struct ifreq ifr;
143 1.1 khorben struct umb_info umbi;
144 1.1 khorben struct umb_parameter umbp;
145 1.1 khorben
146 1.1 khorben if((fd = _umbctl_socket()) < 0)
147 1.1 khorben return 2;
148 1.1 khorben memset(&ifr, 0, sizeof(ifr));
149 1.1 khorben strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
150 1.1 khorben if(argc != 0)
151 1.1 khorben {
152 1.1 khorben memset(&umbp, 0, sizeof(umbp));
153 1.1 khorben ifr.ifr_data = &umbp;
154 1.1 khorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0
155 1.1 khorben || _umbctl_set(ifname, &umbp, argc, argv) != 0
156 1.1 khorben || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM,
157 1.1 khorben &ifr) != 0)
158 1.1 khorben {
159 1.1 khorben close(fd);
160 1.1 khorben return 2;
161 1.1 khorben }
162 1.1 khorben }
163 1.1 khorben if(argc == 0 || verbose > 0)
164 1.1 khorben {
165 1.1 khorben ifr.ifr_data = &umbi;
166 1.1 khorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0)
167 1.1 khorben {
168 1.1 khorben close(fd);
169 1.1 khorben return 3;
170 1.1 khorben }
171 1.1 khorben _umbctl_info(ifname, &umbi);
172 1.1 khorben }
173 1.1 khorben if(close(fd) != 0)
174 1.1 khorben return _error(2, "%s: %s", ifname, strerror(errno));
175 1.1 khorben return 0;
176 1.1 khorben }
177 1.1 khorben
178 1.1 khorben
179 1.1 khorben /* umbctl_file */
180 1.3 khorben static int _umbctl_file(char const * ifname, char const * filename, int verbose)
181 1.1 khorben {
182 1.1 khorben int fd;
183 1.1 khorben struct ifreq ifr;
184 1.1 khorben struct umb_info umbi;
185 1.1 khorben struct umb_parameter umbp;
186 1.1 khorben FILE * fp;
187 1.1 khorben char buf[512];
188 1.1 khorben int eof;
189 1.1 khorben char * tokens[3] = { buf, NULL, NULL };
190 1.1 khorben char * p;
191 1.1 khorben
192 1.1 khorben if((fp = fopen(filename, "r")) == NULL)
193 1.1 khorben return _error(2, "%s: %s", filename, strerror(errno));
194 1.1 khorben memset(&umbp, 0, sizeof(umbp));
195 1.1 khorben while(fgets(buf, sizeof(buf), fp) != NULL)
196 1.1 khorben {
197 1.1 khorben if(buf[0] == '#')
198 1.1 khorben continue;
199 1.1 khorben buf[sizeof(buf) - 1] = '\0';
200 1.1 khorben if((p = strstr(buf, "=")) != NULL)
201 1.1 khorben {
202 1.1 khorben tokens[1] = p + 1;
203 1.1 khorben *p = '\0';
204 1.1 khorben } else
205 1.1 khorben tokens[1] = NULL;
206 1.1 khorben if(_umbctl_set(ifname, &umbp, (p != NULL) ? 2 : 1, tokens) != 0)
207 1.1 khorben break;
208 1.1 khorben }
209 1.1 khorben eof = feof(fp);
210 1.1 khorben if(fclose(fp) != 0 || !eof)
211 1.1 khorben return _error(2, "%s: %s", filename, strerror(errno));
212 1.1 khorben if((fd = _umbctl_socket()) < 0)
213 1.1 khorben return 2;
214 1.1 khorben memset(&ifr, 0, sizeof(ifr));
215 1.1 khorben strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
216 1.1 khorben ifr.ifr_data = &umbp;
217 1.1 khorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0
218 1.1 khorben || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, &ifr) != 0)
219 1.1 khorben {
220 1.1 khorben close(fd);
221 1.1 khorben return 2;
222 1.1 khorben }
223 1.1 khorben if(verbose > 0)
224 1.1 khorben {
225 1.1 khorben ifr.ifr_data = &umbi;
226 1.1 khorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0)
227 1.1 khorben {
228 1.1 khorben close(fd);
229 1.1 khorben return 3;
230 1.1 khorben }
231 1.1 khorben _umbctl_info(ifname, &umbi);
232 1.1 khorben }
233 1.1 khorben if(close(fd) != 0)
234 1.1 khorben return _error(2, "%s: %s", ifname, strerror(errno));
235 1.1 khorben return 0;
236 1.1 khorben }
237 1.1 khorben
238 1.1 khorben
239 1.1 khorben /* umbctl_info */
240 1.1 khorben static void _umbctl_info(char const * ifname, struct umb_info * umbi)
241 1.1 khorben {
242 1.1 khorben char provider[UMB_PROVIDERNAME_MAXLEN + 1];
243 1.1 khorben char pn[UMB_PHONENR_MAXLEN + 1];
244 1.1 khorben char roaming[UMB_ROAMINGTEXT_MAXLEN + 1];
245 1.1 khorben char apn[UMB_APN_MAXLEN + 1];
246 1.1 khorben char fwinfo[UMB_FWINFO_MAXLEN + 1];
247 1.1 khorben char hwinfo[UMB_HWINFO_MAXLEN + 1];
248 1.1 khorben
249 1.1 khorben _utf16_to_char(umbi->provider, UMB_PROVIDERNAME_MAXLEN,
250 1.1 khorben provider, sizeof(provider));
251 1.1 khorben _utf16_to_char(umbi->pn, UMB_PHONENR_MAXLEN, pn, sizeof(pn));
252 1.1 khorben _utf16_to_char(umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN,
253 1.1 khorben roaming, sizeof(roaming));
254 1.1 khorben _utf16_to_char(umbi->apn, UMB_APN_MAXLEN, apn, sizeof(apn));
255 1.1 khorben _utf16_to_char(umbi->fwinfo, UMB_FWINFO_MAXLEN, fwinfo, sizeof(fwinfo));
256 1.1 khorben _utf16_to_char(umbi->hwinfo, UMB_HWINFO_MAXLEN, hwinfo, sizeof(hwinfo));
257 1.1 khorben printf("%s: state %s, mode %s, registration %s\n"
258 1.1 khorben "\tprovider \"%s\", dataclass %s, signal %s\n"
259 1.1 khorben "\tphone number \"%s\", roaming \"%s\" (%s)\n"
260 1.1 khorben "\tAPN \"%s\", TX %" PRIu64 ", RX %" PRIu64 "\n"
261 1.1 khorben "\tfirmware \"%s\", hardware \"%s\"\n",
262 1.1 khorben ifname, umb_val2descr(_umb_state, umbi->state),
263 1.1 khorben umb_val2descr(_umb_regmode, umbi->regmode),
264 1.1 khorben umb_val2descr(_umb_regstate, umbi->regstate), provider,
265 1.1 khorben umb_val2descr(_umb_dataclass, umbi->cellclass),
266 1.1 khorben umb_val2descr(_umb_ber, umbi->ber), pn, roaming,
267 1.1 khorben umbi->enable_roaming ? "allowed" : "denied",
268 1.1 khorben apn, umbi->uplink_speed, umbi->downlink_speed,
269 1.1 khorben fwinfo, hwinfo);
270 1.1 khorben }
271 1.1 khorben
272 1.1 khorben
273 1.1 khorben /* umbctl_ioctl */
274 1.1 khorben static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request,
275 1.1 khorben struct ifreq * ifr)
276 1.1 khorben {
277 1.1 khorben if(ioctl(fd, request, ifr) != 0)
278 1.1 khorben return _error(-1, "%s: %s", ifname, strerror(errno));
279 1.1 khorben return 0;
280 1.1 khorben }
281 1.1 khorben
282 1.1 khorben
283 1.1 khorben /* umbctl_set */
284 1.1 khorben /* callbacks */
285 1.1 khorben static int _set_apn(char const *, struct umb_parameter *, char const *);
286 1.1 khorben static int _set_username(char const *, struct umb_parameter *, char const *);
287 1.1 khorben static int _set_password(char const *, struct umb_parameter *, char const *);
288 1.1 khorben static int _set_pin(char const *, struct umb_parameter *, char const *);
289 1.1 khorben static int _set_puk(char const *, struct umb_parameter *, char const *);
290 1.1 khorben static int _set_roaming_allow(char const *, struct umb_parameter *,
291 1.1 khorben char const *);
292 1.1 khorben static int _set_roaming_deny(char const *, struct umb_parameter *,
293 1.1 khorben char const *);
294 1.1 khorben
295 1.1 khorben static int _umbctl_set(char const * ifname, struct umb_parameter * umbp,
296 1.1 khorben int argc, char * argv[])
297 1.1 khorben {
298 1.1 khorben struct
299 1.1 khorben {
300 1.1 khorben char const * name;
301 1.1 khorben int (*callback)(char const *,
302 1.1 khorben struct umb_parameter *, char const *);
303 1.1 khorben int parameter;
304 1.1 khorben } callbacks[] =
305 1.1 khorben {
306 1.1 khorben { "apn", _set_apn, 1 },
307 1.1 khorben { "username", _set_username, 1 },
308 1.1 khorben { "password", _set_password, 1 },
309 1.1 khorben { "pin", _set_pin, 1 },
310 1.1 khorben { "puk", _set_puk, 1 },
311 1.1 khorben { "roaming", _set_roaming_allow, 0 },
312 1.1 khorben { "-roaming", _set_roaming_deny, 0 },
313 1.1 khorben };
314 1.1 khorben int i;
315 1.1 khorben size_t j;
316 1.1 khorben
317 1.1 khorben for(i = 0; i < argc; i++)
318 1.1 khorben {
319 1.1 khorben for(j = 0; j < sizeof(callbacks) / sizeof(*callbacks); j++)
320 1.1 khorben if(strcmp(argv[i], callbacks[j].name) == 0)
321 1.1 khorben {
322 1.1 khorben if(callbacks[j].parameter && i + 1 == argc)
323 1.1 khorben return _error(-1, "%s: Incomplete"
324 1.1 khorben " parameter", argv[i]);
325 1.1 khorben if(callbacks[j].callback(ifname, umbp,
326 1.1 khorben callbacks[j].parameter
327 1.1 khorben ? argv[i + 1] : NULL))
328 1.1 khorben return -1;
329 1.1 khorben if(callbacks[j].parameter)
330 1.1 khorben i++;
331 1.1 khorben break;
332 1.1 khorben }
333 1.1 khorben if(j == sizeof(callbacks) / sizeof(*callbacks))
334 1.1 khorben return _error(-1, "%s: Unknown parameter", argv[i]);
335 1.1 khorben }
336 1.1 khorben return 0;
337 1.1 khorben }
338 1.1 khorben
339 1.1 khorben static int _set_apn(char const * ifname, struct umb_parameter * umbp,
340 1.1 khorben char const * apn)
341 1.1 khorben {
342 1.1 khorben umbp->apnlen = _char_to_utf16(apn, umbp->apn, sizeof(umbp->apn));
343 1.1 khorben if(umbp->apnlen < 0 || (size_t)umbp->apnlen > sizeof(umbp->apn))
344 1.1 khorben return _error(-1, "%s: %s", ifname, "APN too long");
345 1.1 khorben return 0;
346 1.1 khorben }
347 1.1 khorben
348 1.1 khorben static int _set_username(char const * ifname, struct umb_parameter * umbp,
349 1.1 khorben char const * username)
350 1.1 khorben {
351 1.1 khorben umbp->usernamelen = _char_to_utf16(username, umbp->username,
352 1.1 khorben sizeof(umbp->username));
353 1.1 khorben if(umbp->usernamelen < 0
354 1.1 khorben || (size_t)umbp->usernamelen > sizeof(umbp->username))
355 1.1 khorben return _error(-1, "%s: %s", ifname, "Username too long");
356 1.1 khorben return 0;
357 1.1 khorben }
358 1.1 khorben
359 1.1 khorben static int _set_password(char const * ifname, struct umb_parameter * umbp,
360 1.1 khorben char const * password)
361 1.1 khorben {
362 1.1 khorben umbp->passwordlen = _char_to_utf16(password, umbp->password,
363 1.1 khorben sizeof(umbp->password));
364 1.1 khorben if(umbp->passwordlen < 0
365 1.1 khorben || (size_t)umbp->passwordlen > sizeof(umbp->password))
366 1.1 khorben return _error(-1, "%s: %s", ifname, "Password too long");
367 1.1 khorben return 0;
368 1.1 khorben }
369 1.1 khorben
370 1.1 khorben static int _set_pin(char const * ifname, struct umb_parameter * umbp,
371 1.1 khorben char const * pin)
372 1.1 khorben {
373 1.1 khorben umbp->is_puk = 0;
374 1.1 khorben umbp->op = MBIM_PIN_OP_ENTER;
375 1.1 khorben umbp->pinlen = _char_to_utf16(pin, umbp->pin, sizeof(umbp->pin));
376 1.1 khorben if(umbp->pinlen < 0 || (size_t)umbp->pinlen
377 1.1 khorben > sizeof(umbp->pin))
378 1.1 khorben return _error(-1, "%s: %s", ifname, "PIN code too long");
379 1.1 khorben return 0;
380 1.1 khorben }
381 1.1 khorben
382 1.1 khorben static int _set_puk(char const * ifname, struct umb_parameter * umbp,
383 1.1 khorben char const * puk)
384 1.1 khorben {
385 1.1 khorben umbp->is_puk = 1;
386 1.1 khorben umbp->op = MBIM_PIN_OP_ENTER;
387 1.1 khorben umbp->pinlen = _char_to_utf16(puk, umbp->pin, sizeof(umbp->pin));
388 1.1 khorben if(umbp->pinlen < 0 || (size_t)umbp->pinlen > sizeof(umbp->pin))
389 1.1 khorben return _error(-1, "%s: %s", ifname, "PUK code too long");
390 1.1 khorben return 0;
391 1.1 khorben }
392 1.1 khorben
393 1.1 khorben static int _set_roaming_allow(char const * ifname, struct umb_parameter * umbp,
394 1.1 khorben char const * unused)
395 1.1 khorben {
396 1.1 khorben (void) ifname;
397 1.1 khorben (void) unused;
398 1.1 khorben
399 1.1 khorben umbp->roaming = 1;
400 1.1 khorben return 0;
401 1.1 khorben }
402 1.1 khorben
403 1.1 khorben static int _set_roaming_deny(char const * ifname, struct umb_parameter * umbp,
404 1.1 khorben char const * unused)
405 1.1 khorben {
406 1.1 khorben (void) ifname;
407 1.1 khorben (void) unused;
408 1.1 khorben
409 1.1 khorben umbp->roaming = 0;
410 1.1 khorben return 0;
411 1.1 khorben }
412 1.1 khorben
413 1.1 khorben
414 1.1 khorben /* umbctl_socket */
415 1.1 khorben static int _umbctl_socket(void)
416 1.1 khorben {
417 1.1 khorben int fd;
418 1.1 khorben
419 1.1 khorben if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
420 1.1 khorben return _error(-1, "socket: %s", strerror(errno));
421 1.1 khorben return fd;
422 1.1 khorben }
423 1.1 khorben
424 1.1 khorben
425 1.1 khorben /* usage */
426 1.1 khorben static int _usage(void)
427 1.1 khorben {
428 1.3 khorben fputs("Usage: umbctl [-v] ifname [parameter [value]] [...]\n"
429 1.1 khorben " umbctl -f config-file ifname [...]\n",
430 1.1 khorben stderr);
431 1.1 khorben return 1;
432 1.1 khorben }
433 1.1 khorben
434 1.1 khorben
435 1.1 khorben /* utf16_to_char */
436 1.1 khorben static void _utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen)
437 1.1 khorben {
438 1.1 khorben uint16_t c;
439 1.1 khorben
440 1.1 khorben while (outlen > 0) {
441 1.1 khorben c = inlen > 0 ? htole16(*in) : 0;
442 1.1 khorben if (c == 0 || --outlen == 0) {
443 1.1 khorben /* always NUL terminate result */
444 1.1 khorben *out = '\0';
445 1.1 khorben break;
446 1.1 khorben }
447 1.1 khorben *out++ = isascii(c) ? (char)c : '?';
448 1.1 khorben in++;
449 1.1 khorben inlen--;
450 1.1 khorben }
451 1.1 khorben }
452 1.1 khorben
453 1.1 khorben
454 1.1 khorben /* main */
455 1.1 khorben int main(int argc, char * argv[])
456 1.1 khorben {
457 1.1 khorben int o;
458 1.1 khorben char const * filename = NULL;
459 1.1 khorben int verbose = 0;
460 1.1 khorben
461 1.1 khorben while((o = getopt(argc, argv, "f:v")) != -1)
462 1.1 khorben switch(o)
463 1.1 khorben {
464 1.1 khorben case 'f':
465 1.1 khorben filename = optarg;
466 1.1 khorben break;
467 1.1 khorben case 'v':
468 1.1 khorben verbose++;
469 1.1 khorben break;
470 1.1 khorben default:
471 1.1 khorben return _usage();
472 1.1 khorben }
473 1.1 khorben if(optind == argc)
474 1.1 khorben return _usage();
475 1.1 khorben if(filename != NULL)
476 1.3 khorben {
477 1.3 khorben if(optind + 1 != argc)
478 1.3 khorben return _usage();
479 1.3 khorben return _umbctl_file(argv[optind], filename, verbose);
480 1.3 khorben }
481 1.1 khorben return _umbctl(argv[optind], verbose, argc - optind - 1,
482 1.1 khorben &argv[optind + 1]);
483 1.1 khorben }
484