Mercurial > irccd
view irccdctl/main.c @ 1030:509bcfb14670
misc: change signature to change mode
While here:
- Update manual pages,
- Add missing bits.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 02 Mar 2021 19:34:00 +0100 |
parents | a6ee9827e851 |
children | b460e70a324d |
line wrap: on
line source
/* * main.c -- irccdctl(1) main file * * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <compat.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/un.h> #include <assert.h> #include <ctype.h> #include <err.h> #include <errno.h> #include <limits.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <stdnoreturn.h> #include <string.h> #include <unistd.h> #include <ketopt.h> #include <irccd/limits.h> #include <irccd/util.h> static int verbose; static int sock; static struct sockaddr_un sockaddr = { .sun_family = PF_LOCAL, .sun_path = "/tmp/irccd.sock" }; static char in[IRC_BUF_LEN]; static char out[IRC_BUF_LEN]; static char * poll(void) { static char ret[IRC_BUF_LEN]; char *nl; while (!(nl = strstr(in, "\n"))) { char buf[IRC_BUF_LEN] = {0}; ssize_t nr; if ((nr = recv(sock, buf, sizeof (buf) - 1, 0)) <= 0) errc(1, nr == 0 ? ECONNRESET : errno, "abort"); if (strlcat(in, buf, sizeof (in)) >= sizeof (in)) errc(1, EMSGSIZE, "abort"); } *nl = '\0'; strlcpy(ret, in, sizeof (ret)); memmove(in, nl + 1, sizeof (in) - (nl - in) - 1); return ret; } static void dial(void) { const struct timeval tv = { .tv_sec = 30 }; if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) err(1, "socket"); if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)) < 0 || setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)) < 0) err(1, "setsockopt"); if (connect(sock, (const struct sockaddr *)&sockaddr, SUN_LEN(&sockaddr)) < 0) err(1, "connect"); } static void check(void) { /* Ensure we're talking to irccd. */ int major, minor, patch; if ((sscanf(poll(), "IRCCD %d.%d.%d", &major, &minor, &patch) != 3)) errx(1, "abort: not irccd instance"); if (verbose) printf("connected to irccd %d.%d.%d\n", major, minor, patch); } static void req(const char *fmt, ...) { char buf[IRC_BUF_LEN]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof (buf), fmt, ap); va_end(ap); if (strlcat(out, buf, sizeof (out)) >= sizeof (out) || strlcat(out, "\n", sizeof (out)) >= sizeof (out)) errc(1, EMSGSIZE, "abort"); while (out[0]) { ssize_t ns, len; len = strlen(out); if ((ns = send(sock, out, len, MSG_NOSIGNAL)) <= 0) err(1, "send"); if (ns >= len) memset(out, 0, sizeof (out)); else memmove(out, out + ns, sizeof (out) - ns); } } static char * ok(void) { char *response = poll(); if (strncmp(response, "OK", 2) != 0) errx(1, "abort: %s", response); /* Skip "OK". */ response += 2; while (*response && isspace(*response)) response++; return response; } static void show_connect(char *line) { const char *args[2] = {0}; if (irc_util_split(line, args, 2, ' ') == 2) { printf("%-16s: %s\n", "event", "onConnect"); printf("%-16s: %s\n", "server", args[0]); } } static void show_disconnect(char *line) { const char *args[2] = {0}; if (irc_util_split(line, args, 2, ' ') == 2) { printf("%-16s: %s\n", "event", "onDisonnect"); printf("%-16s: %s\n", "server", args[0]); } } static void show_invite(char *line) { const char *args[5] = {0}; if (irc_util_split(line, args, 5, ' ') == 5) { printf("%-16s: %s\n", "event", "onInvite"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); printf("%-16s: %s\n", "nickname", args[4]); } } static void show_join(char *line) { const char *args[4] = {0}; if (irc_util_split(line, args, 4, ' ') == 4) { printf("%-16s: %s\n", "event", "onJoin"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); } } static void show_kick(char *line) { const char *args[6] = {0}; if (irc_util_split(line, args, 6, ' ') >= 5) { printf("%-16s: %s\n", "event", "onKick"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); printf("%-16s: %s\n", "target", args[4]); printf("%-16s: %s\n", "reason", args[5] ? args[5] : ""); } } static void show_me(char *line) { const char *args[5] = {0}; if (irc_util_split(line, args, 5, ' ') == 5) { printf("%-16s: %s\n", "event", "onMe"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); printf("%-16s: %s\n", "message", args[4]); } } static void show_message(char *line) { const char *args[5] = {0}; if (irc_util_split(line, args, 5, ' ') == 5) { printf("%-16s: %s\n", "event", "onMessage"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); printf("%-16s: %s\n", "message", args[4]); } } static void show_mode(char *line) { const char *args[8] = {0}; if (irc_util_split(line, args, 8, ' ') >= 5) { printf("%-16s: %s\n", "event", "onMode"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); printf("%-16s: %s\n", "mode", args[4]); printf("%-16s: %s\n", "limit", (args[5] ? args[5] : "")); printf("%-16s: %s\n", "user", (args[6] ? args[6] : "")); printf("%-16s: %s\n", "mask", (args[7] ? args[7] : "")); } } static void show_nick(char *line) { const char *args[4] = {0}; if (irc_util_split(line, args, 4, ' ') == 4) { printf("%-16s: %s\n", "event", "onNick"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "nickname", args[3]); } } static void show_notice(char *line) { const char *args[5] = {0}; if (irc_util_split(line, args, 5, ' ') == 5) { printf("%-16s: %s\n", "event", "onNotice"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); printf("%-16s: %s\n", "message", args[4]); } } static void show_part(char *line) { const char *args[5] = {0}; if (irc_util_split(line, args, 5, ' ') >= 4) { printf("%-16s: %s\n", "event", "onPart"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); printf("%-16s: %s\n", "reason", (args[4] ? args[4] : "")); } } static void show_topic(char *line) { const char *args[5] = {0}; if (irc_util_split(line, args, 5, ' ') >= 4) { printf("%-16s: %s\n", "event", "onTopic"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "origin", args[2]); printf("%-16s: %s\n", "channel", args[3]); printf("%-16s: %s\n", "topic", args[4]); } } static void show_whois(char *line) { const char *args[6] = {0}; //char *p, *token; if (irc_util_split(line, args, 6, ' ') >= 4) { printf("%-16s: %s\n", "event", "onWhois"); printf("%-16s: %s\n", "server", args[1]); printf("%-16s: %s\n", "nickname", args[2]); printf("%-16s: %s\n", "username", args[3]); printf("%-16s: %s\n", "hostname", args[4]); printf("%-16s: %s\n", "username", args[5]); //printf("channels: %s\n", args[6]); } } static const struct { const char *event; void (*show)(char *); } watchtable[] = { { "EVENT-CONNECT", show_connect }, { "EVENT-DISCONNECT", show_disconnect }, { "EVENT-INVITE", show_invite }, { "EVENT-JOIN", show_join }, { "EVENT-KICK", show_kick }, { "EVENT-MESSAGE", show_message }, { "EVENT-ME", show_me }, { "EVENT-MODE", show_mode }, { "EVENT-NICK", show_nick }, { "EVENT-NOTICE", show_notice }, { "EVENT-PART", show_part }, { "EVENT-TOPIC", show_topic }, { "EVENT-WHOIS", show_whois } }; static void show(char *ev) { for (size_t i = 0; i < IRC_UTIL_SIZE(watchtable); ++i) { if (strncmp(watchtable[i].event, ev, strlen(watchtable[i].event)) == 0) { watchtable[i].show(ev); printf("\n"); break; } } } static void plugin_list_set(int argc, char **argv, const char *cmd) { char *line, *p; size_t num = 0; if (argc == 3) { req("%s %s %s %s", cmd, argv[0], argv[1], argv[2]); ok(); return; } if (argc == 2) req("%s %s %s", cmd, argv[0], argv[1]); else req("%s %s", cmd, argv[0]); if (sscanf(line = ok(), "%zu", &num) != 1) errx(1, "could not retrieve list"); if (argc == 2) puts(poll()); else { while (num-- != 0 && (line = poll())) { if (!(p = strchr(line, '='))) continue; *p = '\0'; printf("%-16s: %s\n", line, p + 1); } } } static void response_list(const char *cmd) { char *list; req(cmd); if (strncmp(list = poll(), "OK ", 3) != 0) errx(1, "failed to retrieve plugin list"); list += 3; for (char *p; (p = strchr(list, ' ')); ) *p = '\n'; if (*list) puts(list); } static void cmd_hook_add(int argc, char **argv) { (void)argc; req("HOOK-ADD %s %s", argv[0], argv[1]); ok(); } static void cmd_hook_list(int argc, char **argv) { (void)argc; (void)argv; response_list("HOOK-LIST"); } static void cmd_hook_remove(int argc, char **argv) { (void)argc; req("HOOK-REMOVE %s", argv[0]); ok(); } static void cmd_plugin_config(int argc, char **argv) { return plugin_list_set(argc, argv, "PLUGIN-CONFIG"); } /* * Response: * * OK name * summary * version * license * author */ static void cmd_plugin_info(int argc, char **argv) { (void)argc; const char *response; req("PLUGIN-INFO %s", argv[0]); if (strncmp((response = poll()), "OK ", 3) != 0) errx(1, "failed to retrieve plugin information"); printf("%-16s: %s\n", "name", response + 3); printf("%-16s: %s\n", "summary", poll()); printf("%-16s: %s\n", "version", poll()); printf("%-16s: %s\n", "license", poll()); printf("%-16s: %s\n", "author", poll()); } static void cmd_plugin_list(int argc, char **argv) { (void)argc; (void)argv; response_list("PLUGIN-LIST"); } static void cmd_plugin_load(int argc, char **argv) { (void)argc; req("PLUGIN-LOAD %s", argv[0]); ok(); } static void cmd_plugin_path(int argc, char **argv) { return plugin_list_set(argc, argv, "PLUGIN-PATH"); } static void cmd_plugin_reload(int argc, char **argv) { if (argc == 1) req("PLUGIN-RELOAD %s", argv[0]); else req("PLUGIN-RELOAD"); ok(); } static void cmd_plugin_template(int argc, char **argv) { return plugin_list_set(argc, argv, "PLUGIN-TEMPLATE"); } static void cmd_plugin_unload(int argc, char **argv) { if (argc == 1) req("PLUGIN-UNLOAD %s", argv[0]); else req("PLUGIN-UNLOAD"); ok(); } static void cmd_rule_add(int argc, char **argv) { ketopt_t ko = KETOPT_INIT; FILE *fp; char out[IRC_BUF_LEN]; if (!(fp = fmemopen(out, sizeof (out) - 1, "w"))) err(1, "fmemopen"); /* TODO: invalid option. */ for (int ch; (ch = ketopt(&ko, argc, argv, 0, "c:e:i:o:p:s:", NULL)) != -1; ) fprintf(fp, "%c=%s ", ch, ko.arg); argc -= ko.ind; argv += ko.ind; if (argc < 1) errx(1, "missing accept or drop rule action"); fprintf(fp, "%s", argv[0]); if (ferror(fp) || feof(fp)) err(1, "fprintf"); fclose(fp); req("RULE-ADD %s %s", argv[0], out); ok(); } static void cmd_rule_edit(int argc, char **argv) { ketopt_t ko = KETOPT_INIT; FILE *fp; char out[IRC_BUF_LEN]; if (!(fp = fmemopen(out, sizeof (out) - 1, "w"))) err(1, "fmemopen"); /* TODO: invalid option. */ for (int ch; (ch = ketopt(&ko, argc, argv, 0, "a:C:c:E:e:O:o:P:p:S:s:", NULL)) != -1; ) { if (ch == 'a') fprintf(fp, "a=%s ", ko.arg); else fprintf(fp, "%c%c%s ", tolower(ch), isupper(ch) ? '-' : '+', ko.arg); } argc -= ko.ind; argv += ko.ind; if (argc < 1) errx(1, "missing rule index"); if (ferror(fp) || feof(fp)) err(1, "fprintf"); fclose(fp); req("RULE-EDIT %s %s", argv[0], out); ok(); } /* * Response: * * OK <n> * accept * server1 server2 server3 ... * channel1 channel2 channel3 ... * origin1 origin2 origin3 ... * plugin1 plugin2 plugin3 ... * event1 event2 plugin3 ... * (repeat for every rule in <n>) */ static void cmd_rule_list(int argc, char **argv) { (void)argc; (void)argv; size_t num = 0; req("RULE-LIST"); if (sscanf(ok(), "%zu", &num) != 1) errx(1, "could not retrieve rule list"); for (size_t i = 0; i < num; ++i) { printf("%-16s: %zu\n", "index", i); printf("%-16s: %s\n", "action", poll()); printf("%-16s: %s\n", "servers", poll()); printf("%-16s: %s\n", "channels", poll()); printf("%-16s: %s\n", "origins", poll()); printf("%-16s: %s\n", "plugins", poll()); printf("%-16s: %s\n", "events", poll()); printf("\n"); } } static void cmd_rule_move(int argc, char **argv) { (void)argc; long long from, to; const char *errstr; if ((from = strtonum(argv[0], 0, LLONG_MAX, &errstr)) == 0 && errstr) err(1, "%s", argv[0]); if ((to = strtonum(argv[1], 0, LLONG_MAX, &errstr)) == 0 && errstr) err(1, "%s", argv[1]); req("RULE-MOVE %lld %lld", from, to); ok(); } static void cmd_rule_remove(int argc, char **argv) { (void)argc; req("RULE-REMOVE %s", argv[0]); ok(); } static void cmd_server_disconnect(int argc, char **argv) { if (argc == 1) req("SERVER-DISCONNECT %s", argv[0]); else req("SERVER-DISCONNECT"); ok(); } /* * Response: * * OK name * hostname port [ssl] * nickname username realname * chan1 chan2 chanN */ static void cmd_server_info(int argc, char **argv) { (void)argc; char *list; const char *args[16] = {0}; req("SERVER-INFO %s", argv[0]); if (strncmp(list = poll(), "OK ", 3) != 0) errx(1, "failed to retrieve server information"); printf("%-16s: %s\n", "name", list + 3); if (irc_util_split((list = poll()), args, 3, ' ') < 2) errx(1, "malformed server connection"); printf("%-16s: %s\n", "hostname", args[0]); printf("%-16s: %s\n", "port", args[1]); if (args[2]) printf("%-16s: %s\n", "ssl", "true"); if (irc_util_split((list = poll()), args, 3, ' ') != 3) errx(1, "malformed server ident"); printf("%-16s: %s\n", "nickname", args[0]); printf("%-16s: %s\n", "username", args[0]); printf("%-16s: %s\n", "realname", args[0]); printf("%-16s: %s\n", "channels", poll()); } static void cmd_server_join(int argc, char **argv) { if (argc >= 3) req("SERVER-JOIN %s %s %s", argv[0], argv[1], argv[2]); else req("SERVER-JOIN %s %s", argv[0], argv[1]); ok(); } static void cmd_server_list(int argc, char **argv) { (void)argc; (void)argv; response_list("SERVER-LIST"); } static void cmd_server_message(int argc, char **argv) { (void)argc; req("SERVER-MESSAGE %s %s %s", argv[0], argv[1], argv[2]); ok(); } static void cmd_server_me(int argc, char **argv) { (void)argc; req("SERVER-ME %s %s %s", argv[0], argv[1], argv[2]); ok(); } static void cmd_server_mode(int argc, char **argv) { req("SERVER-MODE %s %s %s%c%s", argv[0], argv[1], argv[2], argc >= 4 ? ' ' : '\0', argc >= 4 ? argv[3] : ""); ok(); } static void cmd_server_nick(int argc, char **argv) { (void)argc; req("SERVER-NICK %s %s", argv[0], argv[1]); ok(); } static void cmd_server_notice(int argc, char **argv) { (void)argc; req("SERVER-NOTICE %s %s %s", argv[0], argv[1], argv[2]); ok(); } static void cmd_server_part(int argc, char **argv) { (void)argc; /* Let's advertise irccd a bit. */ req("SERVER-PART %s %s %s", argv[0], argv[1], argc >= 3 ? argv[2] : "irccd is shutting down"); ok(); } static void cmd_server_topic(int argc, char **argv) { (void)argc; req("SERVER-TOPIC %s %s %s", argv[0], argv[1], argv[2]); ok(); } static void cmd_watch(int argc, char **argv) { (void)argc; (void)argv; struct timeval tv = {0}; char *ev; /* Enable watch. */ req("WATCH"); ok(); /* Turn off timeout to receive indefinitely. */ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)) < 0) err(1, "setsockopt"); while ((ev = poll())) show(ev); } static const struct cmd { const char *name; int minargs; int maxargs; void (*exec)(int, char **); } cmds[] = { /* name min max exec */ { "hook-add", 2, 2, cmd_hook_add }, { "hook-list", 0, 0, cmd_hook_list }, { "hook-remove", 1, 1, cmd_hook_remove }, { "plugin-config", 1, 3, cmd_plugin_config }, { "plugin-info", 1, 1, cmd_plugin_info }, { "plugin-list", 0, 0, cmd_plugin_list }, { "plugin-load", 1, 1, cmd_plugin_load }, { "plugin-path", 0, 3, cmd_plugin_path }, { "plugin-reload", 0, 1, cmd_plugin_reload }, { "plugin-template", 1, 3, cmd_plugin_template }, { "plugin-unload", 0, 1, cmd_plugin_unload }, { "rule-add", -1, -1, cmd_rule_add }, { "rule-edit", -1, -1, cmd_rule_edit }, { "rule-list", 0, 0, cmd_rule_list }, { "rule-move", 2, 2, cmd_rule_move }, { "rule-remove", 1, 1, cmd_rule_remove }, { "server-disconnect", 0, 1, cmd_server_disconnect }, { "server-info", 1, 1, cmd_server_info }, { "server-join", 2, 3, cmd_server_join }, { "server-list", 0, 0, cmd_server_list }, { "server-me", 3, 3, cmd_server_me }, { "server-message", 3, 3, cmd_server_message }, { "server-mode", 3, 4, cmd_server_mode }, { "server-nick", 2, 2, cmd_server_nick }, { "server-notice", 3, 3, cmd_server_notice }, { "server-part", 3, 3, cmd_server_part }, { "server-topic", 3, 3, cmd_server_topic }, { "watch", 0, 0, cmd_watch } }; static int cmp_cmd(const void *d1, const void *d2) { return strcmp(d1, ((const struct cmd *)d2)->name); } static const struct cmd * find_cmd(const char *name) { return bsearch(name, cmds, IRC_UTIL_SIZE(cmds), sizeof (cmds[0]), cmp_cmd); } static void run(int argc, char **argv) { const struct cmd *c; if (!(c = find_cmd(argv[0]))) errx(1, "abort: command not found"); --argc; ++argv; if ((c->minargs != -1 && argc < c->minargs) || (c->minargs != -1 && argc > c->maxargs)) errx(1, "abort: invalid number of arguments"); c->exec(argc, argv); } noreturn static void usage(void) { fprintf(stderr, "usage: %s [-v] [-s sock] command [options...] [arguments...]\n", getprogname()); exit(1); } noreturn static void help(void) { fprintf(stderr, "usage: %s hook-add name path\n", getprogname()); fprintf(stderr, " %s hook-list\n", getprogname()); fprintf(stderr, " %s hook-remove id\n", getprogname()); fprintf(stderr, " %s plugin-config id [variable [value]]\n", getprogname()); fprintf(stderr, " %s plugin-info id\n", getprogname()); fprintf(stderr, " %s plugin-list\n", getprogname()); fprintf(stderr, " %s plugin-load name\n", getprogname()); fprintf(stderr, " %s plugin-path [variable [value]]\n", getprogname()); fprintf(stderr, " %s plugin-template [variable [value]]\n", getprogname()); fprintf(stderr, " %s plugin-reload [plugin]\n", getprogname()); fprintf(stderr, " %s plugin-unload [plugin]\n", getprogname()); fprintf(stderr, " %s rule-add [-c channel] [-e event] [-i index] [-o origin] [-p plugin] [-s server] accept|drop\n", getprogname()); fprintf(stderr, " %s rule-edit [-a accept|drop] [-c|C channel] [-e|E event] [-o|O origin] [-s|S server] index\n", getprogname()); fprintf(stderr, " %s rule-list\n", getprogname()); fprintf(stderr, " %s rule-move from to\n", getprogname()); fprintf(stderr, " %s rule-remove index\n", getprogname()); fprintf(stderr, " %s server-connect [-n nickname] [-r realname] [-u username] [-p port] id hostname\n", getprogname()); fprintf(stderr, " %s server-disconnect [server]\n", getprogname()); fprintf(stderr, " %s server-info server\n", getprogname()); fprintf(stderr, " %s server-invite server target channel\n", getprogname()); fprintf(stderr, " %s server-join server channel [password]\n", getprogname()); fprintf(stderr, " %s server-kick server target channel [reason]\n", getprogname()); fprintf(stderr, " %s server-list\n", getprogname()); fprintf(stderr, " %s server-me server target message\n", getprogname()); fprintf(stderr, " %s server-message server target message\n", getprogname()); fprintf(stderr, " %s server-mode server target mode [args]\n", getprogname()); fprintf(stderr, " %s server-nick server nickname\n", getprogname()); fprintf(stderr, " %s server-notice server target message\n", getprogname()); fprintf(stderr, " %s server-part server channel [reason]\n", getprogname()); fprintf(stderr, " %s server-reconnect [server]\n", getprogname()); fprintf(stderr, " %s server-topic server channel topic\n", getprogname()); fprintf(stderr, " %s watch\n", getprogname()); exit(1); } int main(int argc, char **argv) { ketopt_t ko = KETOPT_INIT; setprogname("irccdctl"); --argc; ++argv; for (int ch; (ch = ketopt(&ko, argc, argv, 0, "s:v", NULL)) != -1; ) { switch (ch) { case 's': strlcpy(sockaddr.sun_path, ko.arg, sizeof (sockaddr.sun_path)); break; case 'v': verbose = 1; break; default: usage(); break; } } argc -= ko.ind; argv += ko.ind; if (argc < 1) usage(); else if (strcmp(argv[0], "help") == 0) help(); dial(); check(); run(argc, argv); }