From efe22577dbcd9606bfbfb9b522654b0bd27c8139 Mon Sep 17 00:00:00 2001 From: June McEnroe Date: Sat, 5 Mar 2022 21:40:26 -0500 Subject: Do basic word-wrapping at runtime --- enroll.c | 171 +++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 106 insertions(+), 65 deletions(-) (limited to 'enroll.c') diff --git a/enroll.c b/enroll.c index 9776e80..084e5c1 100644 --- a/enroll.c +++ b/enroll.c @@ -25,6 +25,7 @@ * covered work. */ +#include #include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -57,13 +59,57 @@ static void log(const char *format, ...) { va_end(ap); } +static void wrap(size_t margin, const char *str, size_t len) { + static size_t cols; + if (!cols) { + struct winsize ws; + int error = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); + if (error) { + cols = 80; + } else { + cols = ws.ws_col; + } + } + while (len) { + if (str[0] == ' ') { + str++; + len--; + } + size_t line = cols - margin - 1; + if (line > len) { + line = len; + } else { + while (line && str[line] != ' ') line--; + if (!line) line = len; + } + fwrite(str, line, 1, stdout); + if (line < len) putchar('\n'); + str += line; + len -= line; + } +} + +static void wrapf(const char *format, ...) +__attribute__((format(printf, 1, 2))); +static void wrapf(const char *format, ...) { + va_list ap; + char buf[1024]; + va_start(ap, format); + int len = vsnprintf(buf, sizeof(buf), format, ap); + assert((size_t)len < sizeof(buf)); + va_end(ap); + wrap(0, buf, len); + putchar('\n'); +} + static int prompt1(char **resp, const char *def, const char *desc) { static char *buf; static size_t cap; size_t head = strcspn(desc, "\n"); for (*resp = NULL; !*resp;) { - printf("%.*s [%s] ", (int)head, desc, (def ?: "")); + wrap(strlen(def ?: "") * 2 + 4, desc, head); + printf(" [%s] ", (def ?: "")); ssize_t len = getline(&buf, &cap, stdin); if (len < 0 && feof(stdin)) { printf("\n"); @@ -75,7 +121,9 @@ static int prompt1(char **resp, const char *def, const char *desc) { if (len && buf[len-1] == '\n') buf[len-1] = '\0'; if (buf[0] == '?') { - printf("\n%s\n", &desc[head+1]); + printf("\n"); + wrap(0, &desc[head+1], strlen(&desc[head+1])); + printf("\n\n"); } else if (buf[0]) { *resp = strdup(buf); if (!*resp) err(EX_OSERR, "strdup"); @@ -188,8 +236,8 @@ static int getNetwork(const char *def) { return prompt( &info.network, def, "Which network would you like to connect to?\n" - "Enter the domain name of the IRC network you wish to connect to.\n" - "Examples: tilde.chat, libera.chat\n" + "Enter the domain name of the IRC network you wish to connect to. " + "Examples: tilde.chat, libera.chat " ); } @@ -211,11 +259,9 @@ static int getConfig(const char *configHome) { int pop = prompt( &info.config, prev, "What would you like to name this configuration?\n" - "This is the name you will pass to catgirl to connect to %s.\n" - "Example: $ catgirl %s\n" - "The configuration will be written to:\n" - "%s%s/catgirl\n", - info.network, prev, (suffix != configHome ? "~" : ""), suffix + "This is the name you will pass to catgirl to connect to %s. " + "The configuration will be written to: %s%s/catgirl ", + prev, (suffix != configHome ? "~" : ""), suffix ); free(name); if (pop) return pop; @@ -223,13 +269,12 @@ static int getConfig(const char *configHome) { char path[PATH_MAX]; snprintf(path, sizeof(path), "%s/catgirl/%s", configHome, info.config); if (!access(path, F_OK)) { - printf( - "There is already a configuration named %s.\n" - "Please remove or rename the file:\n" - "%s%s/catgirl/%s\n" - "Otherwise, enter a different name.\n", - info.config, (suffix != configHome ? "~" : ""), suffix, info.config + wrapf("There is already a configuration named %s.", info.config); + wrapf( + "Please remove or rename the file: %s%s/catgirl/%s", + (suffix != configHome ? "~" : ""), suffix, info.config ); + wrapf("Otherwise, enter a different name."); free(info.config); info.config = NULL; } @@ -293,8 +338,8 @@ static int getHostPort(void) { int pop = prompt( &info.host, ircdot, "Which host should catgirl connect to?\n" - "Enter the hostname of the IRC server to connect to. Usually this\n" - "starts with \"irc.\".\n" + "Enter the hostname of the IRC server to connect to. " + "Usually this starts with \"irc.\". " ); free(ircdot); if (pop) return pop; @@ -302,8 +347,8 @@ static int getHostPort(void) { return prompt( &info.port, "6697", "Which port should catgirl connect to?\n" - "Enter the port number of the IRC server. This must be the port for\n" - "TLS, also called SSL.\n" + "Enter the port number of the IRC server. " + "This must be the port for TLS, also called SSL. " ); } @@ -317,7 +362,7 @@ static int getAddr(void) { int error = getaddrinfo(info.host, info.port, &hints, &info.addr); if (error) { log("%s", gai_strerror(error)); - printf("Could not find %s.\n", info.host); + wrapf("Could not find %s.", info.host); return -1; } return 0; @@ -334,8 +379,8 @@ static int connectSock(void) { close(sock); } - printf( - "Could not connect to %s:%s: %s.\n", + wrapf( + "Could not connect to %s:%s: %s.", info.host, info.port, strerror(errno) ); return -1; @@ -352,9 +397,8 @@ static struct tls *connectTLS(int sock) { || tls_handshake(client); if (!error) return client; - printf( - "Could not establish TLS with %s:%s:\n" - "%s\n", + wrapf( + "Could not establish TLS with %s:%s: %s", info.host, info.port, tls_error(client) ); tls_close(client); @@ -386,9 +430,8 @@ static int getTLS(const char *configHome) { close(sock); return 0; } - printf( - "Could not establish TLS with %s:%s:\n" - "%s\n", + wrapf( + "Could not establish TLS with %s:%s: %s", info.host, info.port, tls_error(client) ); @@ -416,11 +459,10 @@ static int getTLS(const char *configHome) { int pop = yesno( &trustIt, true, "Trust the server's self-signed certificate?\n" - "If you are sure you are connecting to the right place, answer yes.\n" - "The current certificate will be saved and explicitly trusted for\n" - "future connections to this network.\n" - "The certificate will be written to:\n" - "%s%s\n", + "If you are sure you are connecting to the right place, answer yes. " + "The current certificate will be saved and explicitly trusted for " + "future connections to this network. " + "The certificate will be written to: %s%s ", (suffix != info.trust ? "~" : ""), suffix ); if (pop || !trustIt) goto fail; @@ -435,8 +477,8 @@ static int getTLS(const char *configHome) { size_t pemLen; const uint8_t *pem = tls_peer_cert_chain_pem(client, &pemLen); if (!pem) { - printf( - "Could not obtain TLS certificate from %s:%s.\n", + wrapf( + "Could not obtain TLS certificate from %s:%s.", info.host, info.port ); goto fail; @@ -474,8 +516,9 @@ static int getAccountName(void) { int pop = prompt( &info.account.name, "", "If you already have an account on %s, what is its name?\n" - "If you have a NickServ account already, enter the name of the\n" - "account. If you aren't sure, leave this blank.\n", + "If you have a NickServ account already, " + "enter the name of the account. " + "If you aren't sure, leave this blank. ", info.network ); if (pop) return pop; @@ -497,9 +540,9 @@ static int getNick(void) { return prompt( &info.nick, getenv("USER"), "What name would you like to use on %s?\n" - "This is the name others will see on IRC, called a nick. It must be\n" - "unique, and can usually contain letters, digits, underscores and\n" - "hyphens, but cannot start with a digit.\n", + "This is the name others will see on IRC, called a nick. " + "It must be unique, and can usually contain letters, digits, " + "underscores and hyphens, but cannot start with a digit. ", info.network ); } @@ -508,9 +551,9 @@ static int getPronouns(void) { return prompt( &info.pronouns, NULL, "What are your pronouns?\n" - "This will be added to your \"real name\" so that other IRC users can\n" - "find out how to refer to you with /whois.\n" - "Examples: they/them, she/her, he/him\n" + "This will be added to your \"real name\" so that other IRC users can " + "find out how to refer to you with /whois. " + "Examples: they/them, she/her, he/him " ); } @@ -539,21 +582,22 @@ int main(int argc, char *argv[]) { if (error && errno != EEXIST) err(EX_CANTCREAT, "%s", path); umask(S_IRWXG | S_IRWXO); - printf( - "Welcome to catgirl enrollment! This tool should get you connected to\n" - "IRC using catgirl :3\n" - "\n" - "Type ? at a prompt for a more detailed explanation.\n" - "The default response is shown in [brackets].\n" - "Press enter at a prompt to select the default response.\n" - "Press control-D at a prompt to return to the previous one.\n" - "\n" + wrapf( + "Welcome to catgirl enrollment! " + "This tool should get you connected to IRC using catgirl :3 " ); + printf("\n"); + wrapf( + "Type ? at a prompt for a more detailed explanation. " + "The default response is shown in [brackets]. " + "Press enter at a prompt to select the default response. " + "Press control-D at a prompt to return to the previous one. " + ); + printf("\n"); int pop; for (;;) { if (!info.network) { - // TODO: Parse ircs: URL in args. pop = getNetwork((optind < argc ? argv[optind] : NULL)); if (pop) return EX_USAGE; } else if (!info.config) { @@ -620,10 +664,10 @@ int main(int argc, char *argv[]) { pop = yesno( &saslPass, true, "Would you like to save your account password?\n" - "If yes, your account password will be written to the configuration\n" - "file and sent automatically using SASL when catgirl connects.\n" - "Otherwise, only your account name will be saved, and catgirl will\n" - "prompt you for the password every time.\n" + "If yes, your account password will be written to the configuration " + "file and sent automatically using SASL when catgirl connects. " + "Otherwise, only your account name will be saved, and catgirl will " + "prompt you for the password every time. " ); if (!pop && saslPass) { fprintf( @@ -641,18 +685,15 @@ int main(int argc, char *argv[]) { error = fclose(file); if (error) err(EX_IOERR, "%s", path); - printf( - "\n" - "You're all set! You can connect to %s using:\n" - "$ catgirl %s\n" - "\n", - info.network, info.config - ); + printf("\n"); + wrapf("You're all set! You can connect to %s using:", info.network); + printf("$ catgirl %s\n\n", info.config); bool run; pop = yesno( &run, true, "Connect now?\n" - "There's nothing left to do. This will start catgirl.\n" + "There's nothing left to do. " + "This will start catgirl. " ); if (pop || !run) return EX_OK; -- cgit 1.4.1