diff options
Diffstat (limited to '')
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | README.7 | 33 | ||||
-rw-r--r-- | buffer.c | 9 | ||||
-rw-r--r-- | catgirl.1 | 504 | ||||
-rw-r--r-- | chat.c | 49 | ||||
-rw-r--r-- | chat.h | 7 | ||||
-rw-r--r-- | command.c | 8 | ||||
-rw-r--r-- | complete.c | 7 | ||||
-rw-r--r-- | config.c | 14 | ||||
-rwxr-xr-x | configure | 7 | ||||
-rw-r--r-- | filter.c | 9 | ||||
-rw-r--r-- | handle.c | 25 | ||||
-rw-r--r-- | input.c | 23 | ||||
-rw-r--r-- | irc.c | 37 | ||||
-rw-r--r-- | log.c | 29 | ||||
-rw-r--r-- | sandman.m | 13 | ||||
-rw-r--r-- | scripts/build-chroot.sh | 74 | ||||
-rw-r--r-- | scripts/chroot-man.sh | 2 | ||||
-rw-r--r-- | scripts/chroot-prompt.sh | 7 | ||||
-rw-r--r-- | scripts/reconnect.sh | 10 | ||||
-rw-r--r-- | scripts/sshd_config | 9 | ||||
-rw-r--r-- | ui.c | 25 | ||||
-rw-r--r-- | url.c | 35 | ||||
-rw-r--r-- | window.c | 12 | ||||
-rw-r--r-- | xdg.c | 3 |
25 files changed, 483 insertions, 481 deletions
diff --git a/Makefile b/Makefile index 66fb408..e08e8e3 100644 --- a/Makefile +++ b/Makefile @@ -72,16 +72,3 @@ install: ${BINS} ${MANS} uninstall: rm -f ${BINS:%=${DESTDIR}${BINDIR}/%} rm -f ${MANS:%=${DESTDIR}${MANDIR}/man1/%} - -CHROOT_USER = chat -CHROOT_GROUP = ${CHROOT_USER} - -chroot.tar: catgirl catgirl.1 scripts/chroot-prompt.sh scripts/chroot-man.sh -chroot.tar: scripts/build-chroot.sh - sh scripts/build-chroot.sh ${CHROOT_USER} ${CHROOT_GROUP} - -install-chroot: chroot.tar - tar -px -f chroot.tar -C /home/${CHROOT_USER} - -clean-chroot: - rm -fr chroot.tar root diff --git a/README.7 b/README.7 index a26d270..c4f82e8 100644 --- a/README.7 +++ b/README.7 @@ -1,5 +1,5 @@ .\" To view this file: $ man ./README.7 -.Dd July 9, 2023 +.Dd May 8, 2025 .Dt README 7 .Os "Causal Agency" . @@ -88,7 +88,7 @@ Reconnection: when the connection to the server is lost, .Nm exits. -It can be run in a loop +She can be run in a loop or connected to a bouncer, such as .Lk https://git.causal.agency/pounce "pounce" . @@ -109,6 +109,10 @@ TLS is now ubiquitous and certificates are easy to obtain. .El . +.Sh TESTIMONIALS +.Dq catgirl has like the best scrolling i've ever used in a terminal application +.D1 \(em my friend kylie +. .Sh INSTALLING .Nm requires ncurses and @@ -173,14 +177,14 @@ wrapper is provided for macOS to stop and start .Nm on system sleep and wake. -To enable it, +To enable him, configure with: .Bd -literal -offset indent $ ./configure --enable-sandman .Ed . .Sh FILES -.Bl -tag -width "command.c" -compact +.Bl -tag -width "complete.c" -compact .It Pa chat.h global state and declarations .It Pa chat.c @@ -224,22 +228,13 @@ example .Xr tmux 1 configuration for multiple networks and automatic reconnects +.It Pa scripts/reconnect.sh +example script to restart +.Xr catgirl 1 +when she gets disconnected .It Pa scripts/notify-send.scpt .Xr notify-send 1 in AppleScript -.It Pa scripts/build-chroot.sh -chroot builder for -.Ox -and -.Fx -.It Pa scripts/chroot-prompt.sh -name prompt wrapper for chroot -.It Pa scripts/chroot-man.sh -.Xr man 1 -implementation for chroot -.It Pa scripts/sshd_config -.Xr sshd 8 -configuration for public chroot .El . .Sh CONTRIBUTING @@ -252,10 +247,6 @@ For sending patches by email, see Mailing list archives are available at .Aq Lk https://causal.agency/list/catgirl.html . . -.Pp -Monetary contributions can be -.Lk https://liberapay.com/june/donate "donated via Liberapay" . -. .Sh SEE ALSO .Xr catgirl 1 , .Xr sandman 1 diff --git a/buffer.c b/buffer.c index f82e553..6a7b412 100644 --- a/buffer.c +++ b/buffer.c @@ -30,7 +30,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include <time.h> #include <wchar.h> #include <wctype.h> @@ -50,7 +49,7 @@ struct Buffer { struct Buffer *bufferAlloc(void) { struct Buffer *buffer = calloc(1, sizeof(*buffer)); - if (!buffer) err(EX_OSERR, "calloc"); + if (!buffer) err(1, "calloc"); return buffer; } @@ -107,7 +106,7 @@ static int flow(struct Lines *hard, int cols, const struct Line *soft) { line->heat = soft->heat; line->time = soft->time; line->str = strdup(soft->str); - if (!line->str) err(EX_OSERR, "strdup"); + if (!line->str) err(1, "strdup"); int width = 0; int align = 0; @@ -185,7 +184,7 @@ static int flow(struct Lines *hard, int cols, const struct Line *soft) { size_t cap = StyleCap + align + strlen(&wrap[n]) + 1; line->str = malloc(cap); - if (!line->str) err(EX_OSERR, "malloc"); + if (!line->str) err(1, "malloc"); char *end = &line->str[cap]; str = seprintf(line->str, end, "%*s", (width = align), ""); @@ -209,7 +208,7 @@ int bufferPush( soft->heat = heat; soft->time = time; soft->str = strdup(str); - if (!soft->str) err(EX_OSERR, "strdup"); + if (!soft->str) err(1, "strdup"); if (heat < thresh) return 0; return flow(&buffer->hard, cols, soft); } diff --git a/catgirl.1 b/catgirl.1 index 815eade..f2a2fbb 100644 --- a/catgirl.1 +++ b/catgirl.1 @@ -1,4 +1,4 @@ -.Dd October 11, 2023 +.Dd May 24, 2024 .Dt CATGIRL 1 .Os . @@ -46,9 +46,9 @@ The .Nm IRC client -provides a curses interface -for TLS-only -Internet Relay Chat. +provides a curses interface for +Internet Relay Chat +over TLS. The only required option is .Fl h , the host name to connect to. @@ -62,7 +62,8 @@ in to view the list of .Sx COMMANDS and -.Sx KEY BINDINGS . +.Sx KEY BINDINGS +in this manual. . .Pp Options can be loaded from files @@ -78,50 +79,65 @@ unless the path starts with .Ql \&./ or .Ql \&../ . -Files and flags listed later -on the command line -take precedence over -those listed earlier. +For example, +a configuration file at +.Pa ~/.config/catgirl/tilde +can be loaded by running +.Ql catgirl tilde . . .Pp Each option is placed on a line, and lines beginning with .Ql # are ignored. +An optional +.Ql = +may appear +between an option and its value. The options are listed below following their corresponding flags. +Flags and options in files are processed +in the order they appear on the command line, +so later values override earlier values. . .Bl -tag -width Ds -.It Fl C Ar util | Cm copy No = Ar util -Set the utility used by -.Ic /copy . +.It Fl C Ar util | Cm copy Ar util +Set the utility used by the +.Ic /copy +command. Subsequent .Cm copy -options append arguments to -.Ar util . -The URL to copy is provided to -.Ar util -on standard input. +options add arguments +to the utility. +The URL to copy is provided +to the utility on standard input. The default is the first available of .Xr pbcopy 1 , .Xr wl-copy 1 , -.Xr xclip 1 , +.Xr xclip 1 +or .Xr xsel 1 . . -.It Fl H Ar seed,bound | Cm hash No = Ar seed,bound -Set the initial seed -of the nick and channel -color hash function -and the maximum IRC color value -produced by the function. -The default is 0,75. -To use only colors from -the 16-color terminal set, +.It Fl H Ar seed,bound | Cm hash Ar seed,bound +Set the seed for choosing +nick and channel colours +and the maximum IRC colour value +that will be chosen. +Changing the seed +will randomize the chosen colours, +in case you don't like the ones +chosen for yourself or your crush. +.Pp +The default is 0,75, +which uses colours +in the 256-colour terminal set. +To use only colours +from the 16-colour terminal set, use 0,15. -To disable nick and channel colors, +To disable nick and channel colours, use 0,0. . -.It Fl I Ar pattern | Cm highlight No = Ar pattern +.It Fl I Ar pattern | Cm highlight Ar pattern Add a case-insensitive message highlight pattern, which may contain .Ql * , @@ -132,44 +148,51 @@ wildcards as in .Xr glob 7 . The format of the pattern is as follows: .Bd -ragged -offset indent +.\" FIXME: there's really no reason !user@host should be required .Ar nick Ns Op Ar !user@host Op Ar command Op Ar channel Op Ar message .Ed .Pp The commands which can be matched are: -.Sy INVITE , -.Sy JOIN , -.Sy NICK , -.Sy NOTICE , -.Sy PART , -.Sy PRIVMSG , -.Sy QUIT , -.Sy SETNAME . +.Sy invite , +.Sy join , +.Sy nick , +.Sy notice , +.Sy part , +.Sy privmsg , +.Sy quit , +.Sy setname . +.Pp +For example, +to highlight whenever your crush +joins your favourite channel: +.Pp +.Dl highlight crush!*@* join #channel . -.It Fl N Ar util | Cm notify No = Ar util +.It Fl N Ar util | Cm notify Ar util Send notifications using a utility. Subsequent .Cm notify -options append arguments to -.Ar util . +options add arguments +to the utility. The window name and message -are provided to -.Ar util +are provided to the utility as two additional arguments, appropriate for .Xr notify-send 1 . . -.It Fl O Ar util | Cm open No = Ar util -Set the utility used by -.Ic /open . +.It Fl O Ar util | Cm open Ar util +Set the utility used by the +.Ic /open +command. Subsequent .Cm open -options append arguments to -.Ar util . -The URL to open is provided to -.Ar util -as an argument. +options add arguments +to the utility. +The URL to open is provided +to the utility as an additional argument. The default is the first available of -.Xr open 1 , +.Xr open 1 +or .Xr xdg-open 1 . . .It Fl R | Cm restrict @@ -185,58 +208,75 @@ option, and viewing this manual with .Ic /help . . -.It Fl S Ar host | Cm bind No = Ar host +.It Fl S Ar host | Cm bind Ar host Bind to source address .Ar host when connecting to the server. -To connect from any address -over IPv4 only, +To connect from any IPv4 address, use 0.0.0.0. -To connect from any address -over IPv6 only, +To connect from any IPv6 address, use ::. . -.It Fl T Ns Oo Ar format Oc | Cm timestamp Op = Ar format -Show timestamps by default, -in the specified -.Xr strftime 3 -.Ar format . -The format string may contain -raw IRC formatting codes. -The default format is -.Qq \&%X . +.It Fl T Ns Oo Ar format Oc | Cm timestamp Op Ar format +Show timestamps by default. +The optional +.Ar format +string is interpreted by +.Xr strftime 3 . +The string may contain +raw IRC formatting codes, +if you can figure out +how to enter them. . -.It Fl a Ar user : Ns Ar pass | Cm sasl-plain No = Ar user : Ns Ar pass -Authenticate as -.Ar user -with -.Ar pass -using SASL PLAIN. +.It Fl a Ar user : Ns Ar pass | Cm sasl-plain Ar user : Ns Ar pass +Authenticate with NickServ +during connection using SASL PLAIN. +.Nm +will disconnect +if authentication fails. Leave .Ar pass -blank to prompt for the password. +blank to prompt for the password when +.Nm +starts. . -.It Fl c Ar path | Cm cert No = Ar path -Load the TLS client certificate from -.Ar path . -The -.Ar path -is searched for in the same manner -as configuration files. -If the private key is in a separate file, -it is loaded with -.Cm priv . -With -.Cm sasl-external , -authenticate using SASL EXTERNAL. -Certificates can be generated with -.Fl g . +.It Fl c Ar path | Cm cert Ar path +Connect using a TLS client certificate +loaded from +.Ar path , +which is searched for +in the same manner as configuration files. +If the private key +is in a separate file, +additionally specify it with the +.Cm priv +option. +.Pp +To use this certificate +to authenticate to NickServ +using CertFP, +use the +.Cm sasl-external +option. +See +.Sx Configuring CertFP . +.Pp +Client certificates +can be generated with the +.Fl g +flag. . .It Fl e | Cm sasl-external -Authenticate using SASL EXTERNAL, -also known as CertFP. -The TLS client certificate is loaded with -.Cm cert . +Authenticate to NickServ +during connection using CertFP +via SASL EXTERNAL. +.Nm +will disconnect +if authentication fails. +The client certificate +must be specified with the +.Cm cert +option. See .Sx Configuring CertFP . . @@ -246,11 +286,11 @@ Generate a TLS client certificate using and write it to .Ar path . . -.It Fl h Ar host | Cm host No = Ar host -Connect to +.It Fl h Ar host | Cm host Ar host +Connect to the IRC server .Ar host . . -.It Fl i Ar pattern | Cm ignore No = Ar pattern +.It Fl i Ar pattern | Cm ignore Ar pattern Add a case-insensitive message ignore pattern, which may contain .Ql * , @@ -265,56 +305,69 @@ The format of the pattern is as follows: .Ed .Pp The commands which can be matched are: -.Sy INVITE , -.Sy JOIN , -.Sy NICK , -.Sy NOTICE , -.Sy PART , -.Sy PRIVMSG , -.Sy QUIT , -.Sy SETNAME . +.Sy invite , +.Sy join , +.Sy nick , +.Sy notice , +.Sy part , +.Sy privmsg , +.Sy quit , +.Sy setname . +.Pp +Visibility of ignored messages +can be toggled using +.Ic M-- +and +.Ic M-+ . . -.It Fl j Ar channels Oo Ar keys Oc | Cm join No = Ar channels Oo Ar keys Oc +.It Fl j Ar channels Oo Ar keys Oc | Cm join Ar channels Oo Ar keys Oc Join the comma-separated list of .Ar channels with the optional comma-separated list of channel .Ar keys . +No spaces may appear in either list. . -.It Fl k Ar path | Cm priv No = Ar priv -Load the TLS client private key from -.Ar path . -The -.Ar path -is searched for in the same manner -as configuration files. +.It Fl k Ar path | Cm priv Ar path +Load the TLS client private key +for a certificate loaded with the +.Cm cert +option from +.Ar path , +which is search for +in the same manner as configuration files. . .It Fl l | Cm log -Log chat events to files in paths -.Pa $XDG_DATA_HOME/catgirl/log/network/channel/YYYY-MM-DD.log . +Log messages to files in +.Pa $XDG_DATA_HOME/catgirl/log +.Po +usually +.Pa ~/.local/share/catgirl/log +.Pc . +Directories are created +for each network and channel, +and files are created for each date +in the format +.Pa YYYY-MM-DD.log . . -.It Fl m Ar mode | Cm mode No = Ar mode -Set the user -.Ar mode . +.It Fl m Ar modes | Cm mode Ar modes +Set user modes as soon as possible +after connecting. . -.It Fl n Ar nick Oo Ar ... Oc | Cm nick No = Ar nick Oo Ar ... Oc -Set nickname to -.Ar nick . -The default nickname is -the value of the environment variable +.It Fl n Ar nick Oo Ar ... Oc | Cm nick Ar nick Oo Ar ... Oc +Set the nickname with optional fallbacks, +should one nick be unavailable. +Each nick is treated as a highlight word. +The default nickname is the value of .Ev USER . -Additional space-separated nicks -will be tried in order -if the first is not available, -and all nicks -are treated as highlight words. . .It Fl o -Print the server certificate chain -to standard output in PEM format -and exit. +Connect to the server +only to obtain its certificate chain +and write it to standard output +in PEM format. . -.It Fl p Ar port | Cm port No = Ar port -Connect to +.It Fl p Ar port | Cm port Ar port +Connect to the IRC server on .Ar port . The default port is 6697. . @@ -323,17 +376,27 @@ Raise the default message visibility threshold for new windows, hiding general events (joins, quits, etc.). +The threshold can be lowered with +.Ic M-- . . -.It Fl r Ar real | Cm real No = Ar real -Set realname to -.Ar real . -The default realname is the same as the nickname. +.It Fl r Ar real | Cm real Ar real +Set the +.Dq realname +which appears in +.Ic /whois . +The default is the same as the nickname. +This is a good place to add your pronouns. . -.It Fl s Ar name | Cm save No = Ar name -Save and load the contents of windows from +.It Fl s Ar name | Cm save Ar name +Persist windows and their scrollback +in a file called .Ar name in -.Pa $XDG_DATA_DIRS/catgirl , +.Pa $XDG_DATA_DIRS/catgirl +.Po +usually +.Pa ~/.local/share/catgirl +.Pc , or an absolute or relative path if .Ar name starts with @@ -342,39 +405,50 @@ starts with or .Ql \&../ . . -.It Fl t Ar path | Cm trust No = Ar path -Trust the self-signed certificate -loaded from -.Ar path -and disable server name verification. -The -.Ar path -is searched for in the same manner -as configuration files. +.It Fl t Ar path | Cm trust Ar path +Trust the self-signed certificate in +.Ar path , +which is searched for +in the same manner as configuration files. +Server name verification is also disabled. See .Sx Connecting to Servers with Self-signed Certificates . . -.It Fl u Ar user | Cm user No = Ar user -Set username to -.Ar user . -The default username is the same as the nickname. +.It Fl u Ar user | Cm user Ar user +Set the username. +This is almost entirely irrelevant, +except that it's more likely to remain stable, +and +.Nm +uses it to choose nick colours. +The default is the same as the nickname. . .It Fl v | Cm debug -Log raw IRC messages to the +Log raw IRC protocol to the .Sy <debug> -window +window, as well as standard error if it is not a terminal. . -.It Fl w Ar pass | Cm pass No = Ar pass -Log in with the server password -.Ar pass . +.It Fl w Ar pass | Cm pass Ar pass +Connect using a server password. Leave .Ar pass -blank to prompt for the password. +blank +.Po +using an +.Ql = +.Pc +to prompt for the password when +.Nm +starts. .El . .Ss Configuring CertFP +CertFP allows you to +authenticate with NickServ during connection +using a TLS client certificate +rather than your account password. .Bl -enum .It Generate a new TLS client certificate: @@ -382,32 +456,34 @@ Generate a new TLS client certificate: $ catgirl -g ~/.config/catgirl/example.pem .Ed .It -Connect to the server using the certificate: +Connect to the server using the certificate +by adding the following configuration: .Bd -literal -offset indent -cert = example.pem -# or: $ catgirl -c example.pem +cert example.pem .Ed .It -Identify with services or use -.Cm sasl-plain , +Identify with NickServ, then add the certificate fingerprint to your account: .Bd -literal -offset indent /ns CERT ADD .Ed .It -Enable SASL EXTERNAL +Enable SASL EXTERNAL in your configuration to require successful authentication when connecting (not possible on all networks): .Bd -literal -offset indent -cert = example.pem +cert example.pem sasl-external -# or: $ catgirl -e -c example.pem .Ed .El . .Ss Connecting to Servers with Self-signed Certificates +If connecting to a server fails +with a certificate verification error +due to a self-signed certificate, +it needs to be trusted manually. .Bl -enum .It Connect to the server @@ -420,8 +496,7 @@ Configure .Nm to trust the certificate: .Bd -literal -offset indent -trust = example.pem -# or: $ catgirl -t example.pem +trust example.pem .Ed .El . @@ -429,13 +504,13 @@ trust = example.pem The .Nm interface is split -into three areas. +into three main areas. . .Ss Status Line The top line of the terminal shows window statuses. Only the currently active window -and windows with activity are listed. +and windows with activity are shown. The status line for a window might look like this: .Bd -literal -offset indent @@ -446,7 +521,8 @@ The number on the left is the window number. Following it may be one of .Ql - , -.Ql + , +.Ql + +or .Ql ++ , as well as .Ql = . @@ -462,11 +538,11 @@ indicates the number of unread messages. The number following .Ql ~ indicates how many lines -are below the scroll position. +are below the current scroll position. An .Ql @ indicates that there is unsent input -in the window's +waiting in the window's .Sx Input Line . .Pp .Nm @@ -496,7 +572,11 @@ with the nick between hyphens. .Pp Blank lines are inserted into the chat -as unread markers. +as unread markers +whenever there are messages +in a window that is not active +or the terminal is not focused +(in some terminal emulators). .Pp While scrolling, the most recent 5 lines of chat @@ -524,68 +604,106 @@ starting at the point where it will be split into a second message. . .Sh COMMANDS -Any unique prefix can be used to abbreviate a command. +Commands can be abbreviated +if no other command +shares the same prefix. For example, .Ic /join can be typed -.Ic /j . +.Ic /j , +and +.Ic /window +can be typed +.Ic /wi . . .Ss Chat Commands .Bl -tag -width Ds .It Ic /away Op Ar message Set or clear your away status. +This is sent in reply to private messages +and shown in +.Ic /whois . .It Ic /cs Ar command -Send a command to ChanServ. +Send a command to ChanServ, +the service for managing registered channels. .It Ic /invite Ar nick -Invite a user to the channel. +Invite someone to the channel. .It Ic /join Op Ar channel Op Ar key Join the named channel, -the current channel, +the current channel (if you've left), or the channel you've been invited to. -.It Ic /list Op Ar channel -List channels. +.It Ic /list Op Ar search +List channels, their user counts and their topics. +The +.Ar search +can usually contain glob-style wildcards. .It Ic /me Op Ar action Send an action message. +These are used to write messages in third person. .It Ic /msg Ar nick message -Send a private message. +Send a private message to someone. .It Ic /names -List users in the channel. +List the users in the channel. .It Ic /nick Ar nick -Change nicknames. +Change your nickname. .It Ic /notice Ar message Send a notice. +It's best not to do this. .It Ic /ns Ar command -Send a command to NickServ. +Send a command to NickServ, +the service for managing your account. .It Ic /ops List channel operators. +They can kick or ban someone from the channel. .It Ic /part Op Ar message Leave the channel. +Use +.Ic /close +if you want to close the window afterwards. .It Ic /query Ar nick -Start a private conversation. +Start a private conversation with someone. .It Ic /quit Op Ar message -Quit IRC. +Disconnect from IRC and close +.Nm . +You can do this even quicker with +.Ic C-c . .It Ic /quote Ar command Send a raw IRC command. -Use +Often +.Nm +will not know how to interpret the results. +You can use .Ic M-- -to show unknown replies. +to show unknown server responses +in the +.Sy <network> +or channel windows. .It Ic /say Ar message Send a regular message. +This is useful +if the message you want to send +begins with a slash. .It Ic /setname Ar name -Update realname -if supported by the server. +Update your +.Dq realname +if the server supports it. +This may be broadcast +to other users +with clients that support it. .It Ic /topic Op Ar topic Show or set the topic of the channel. Press .Ic Tab -twice to copy the current topic. +twice immediately after +.Ic /topic +to copy the current topic. .It Ic /whois Op Ar nick Query information about a user or yourself. .It Ic /whowas Ar nick Query past information about a user. .El . -.Ss UI Commands +.Ss Interface Commands .Bl -tag -width Ds .It Ic /close Op Ar name | num Close the named, numbered or current window. @@ -909,9 +1027,7 @@ The .Nm client exits 0 if requested by the user, -.Dv EX_UNAVAILABLE -(69) -if the connection is lost, +69 if the connection is lost, and >0 if any other error occurs. . .Sh EXAMPLES diff --git a/chat.c b/chat.c index 6728240..bc23c3f 100644 --- a/chat.c +++ b/chat.c @@ -41,7 +41,6 @@ #include <sys/stat.h> #include <sys/time.h> #include <sys/wait.h> -#include <sysexits.h> #include <time.h> #include <tls.h> #include <unistd.h> @@ -70,7 +69,7 @@ static void genCert(const char *path) { "-nodes", "-subj", subj, "-out", path, "-keyout", path, NULL ); - err(EX_UNAVAILABLE, "openssl"); + err(127, "openssl"); } char *idNames[IDCap] = { @@ -93,7 +92,7 @@ static void exitSave(void) { int error = uiSave(); if (error) { warn("%s", save); - _exit(EX_IOERR); + _exit(1); } } @@ -104,7 +103,7 @@ int utilPipe[2] = { -1, -1 }; static void execRead(void) { char buf[1024]; ssize_t len = read(execPipe[0], buf, sizeof(buf) - 1); - if (len < 0) err(EX_IOERR, "read"); + if (len < 0) err(1, "read"); if (!len) return; buf[len] = '\0'; for (char *ptr = buf; ptr;) { @@ -116,7 +115,7 @@ static void execRead(void) { static void utilRead(void) { char buf[1024]; ssize_t len = read(utilPipe[0], buf, sizeof(buf) - 1); - if (len < 0) err(EX_IOERR, "read"); + if (len < 0) err(1, "read"); if (!len) return; buf[len] = '\0'; for (char *ptr = buf; ptr;) { @@ -135,7 +134,7 @@ static void parseHash(char *str) { static void parsePlain(char *str) { self.plainUser = strsep(&str, ":"); - if (!str) errx(EX_USAGE, "SASL PLAIN missing colon"); + if (!str) errx(1, "SASL PLAIN missing colon"); self.plainPass = str; } @@ -159,27 +158,27 @@ static void sandboxEarly(bool log) { if (log) { char buf[PATH_MAX]; int error = unveil(dataPath(buf, sizeof(buf), "log", 0), "wc"); - if (error) err(EX_OSERR, "unveil"); + if (error) err(1, "unveil"); ptr = seprintf(ptr, end, " wpath cpath"); } if (!self.restricted) { int error = unveil("/", "x"); - if (error) err(EX_OSERR, "unveil"); + if (error) err(1, "unveil"); ptr = seprintf(ptr, end, " proc exec"); } promisesInitial = ptr; ptr = seprintf(ptr, end, " inet dns"); int error = pledge(promises, NULL); - if (error) err(EX_OSERR, "pledge"); + if (error) err(1, "pledge"); } static void sandboxLate(int irc) { (void)irc; *promisesInitial = '\0'; int error = pledge(promises, NULL); - if (error) err(EX_OSERR, "pledge"); + if (error) err(1, "pledge"); } #elif defined __FreeBSD__ @@ -202,7 +201,7 @@ static void sandboxLate(int irc) { || caph_rights_limit( irc, cap_rights_init(&rights, CAP_SEND, CAP_RECV, CAP_EVENT) ); - if (error) err(EX_OSERR, "cap_rights_limit"); + if (error) err(1, "cap_rights_limit"); // caph_cache_tzdata(3) doesn't load UTC info, which we need for // certificate verification. gmtime(3) does. @@ -210,7 +209,7 @@ static void sandboxLate(int irc) { gmtime(&(time_t) { time(NULL) }); error = cap_enter(); - if (error) err(EX_OSERR, "cap_enter"); + if (error) err(1, "cap_enter"); } #else @@ -317,40 +316,40 @@ int main(int argc, char *argv[]) { break; case 'u': user = optarg; break; case 'v': self.debug = true; break; case 'w': pass = optarg; - break; default: return EX_USAGE; + break; default: return 1; } } - if (!host) errx(EX_USAGE, "host required"); + if (!host) errx(1, "host required"); if (printCert) { #ifdef __OpenBSD__ int error = pledge("stdio inet dns", NULL); - if (error) err(EX_OSERR, "pledge"); + if (error) err(1, "pledge"); #endif ircConfig(true, NULL, NULL, NULL); ircConnect(bind, host, port); ircPrintCert(); ircClose(); - return EX_OK; + return 0; } if (!self.nicks[0]) self.nicks[0] = getenv("USER"); - if (!self.nicks[0]) errx(EX_CONFIG, "USER unset"); + if (!self.nicks[0]) errx(1, "USER unset"); if (!user) user = self.nicks[0]; if (!real) real = self.nicks[0]; if (pass && !pass[0]) { char *buf = malloc(512); - if (!buf) err(EX_OSERR, "malloc"); + if (!buf) err(1, "malloc"); pass = readpassphrase("Server password: ", buf, 512, 0); - if (!pass) errx(EX_IOERR, "unable to read passphrase"); + if (!pass) errx(1, "unable to read passphrase"); } if (self.plainPass && !self.plainPass[0]) { char *buf = malloc(512); - if (!buf) err(EX_OSERR, "malloc"); + if (!buf) err(1, "malloc"); self.plainPass = readpassphrase("Account password: ", buf, 512, 0); - if (!self.plainPass) errx(EX_IOERR, "unable to read passphrase"); + if (!self.plainPass) errx(1, "unable to read passphrase"); } // Modes defined in RFC 1459: @@ -411,7 +410,7 @@ int main(int argc, char *argv[]) { if (!self.restricted) { int error = pipe(utilPipe) || pipe(execPipe); - if (error) err(EX_OSERR, "pipe"); + if (error) err(1, "pipe"); fcntl(utilPipe[0], F_SETFD, FD_CLOEXEC); fcntl(utilPipe[1], F_SETFD, FD_CLOEXEC); @@ -428,7 +427,7 @@ int main(int argc, char *argv[]) { }; while (!self.quit) { int nfds = poll(fds, (self.restricted ? 2 : ARRAY_LEN(fds)), -1); - if (nfds < 0 && errno != EINTR) err(EX_IOERR, "poll"); + if (nfds < 0 && errno != EINTR) err(1, "poll"); if (nfds > 0) { if (fds[0].revents) inputRead(); if (fds[1].revents) ircRecv(); @@ -446,12 +445,12 @@ int main(int argc, char *argv[]) { .it_interval.tv_sec = 30, }; int error = setitimer(ITIMER_REAL, &timer, NULL); - if (error) err(EX_OSERR, "setitimer"); + if (error) err(1, "setitimer"); } if (signals[SIGALRM]) { signals[SIGALRM] = 0; if (ping) { - errx(EX_UNAVAILABLE, "ping timeout"); + errx(69, "ping timeout"); } else { ircFormat("PING nyaa\r\n"); ping = true; diff --git a/chat.h b/chat.h index 2a41cf6..369747c 100644 --- a/chat.h +++ b/chat.h @@ -35,7 +35,6 @@ #include <stdio.h> #include <string.h> #include <strings.h> -#include <sysexits.h> #include <time.h> #include <wchar.h> @@ -131,7 +130,7 @@ static inline uint idFor(const char *name) { if (idNext == IDCap) return Network; idNames[idNext] = strdup(name); idColors[idNext] = Default; - if (!idNames[idNext]) err(EX_OSERR, "strdup"); + if (!idNames[idNext]) err(1, "strdup"); return idNext++; } @@ -221,7 +220,7 @@ extern struct Self { static inline void set(char **field, const char *value) { free(*field); *field = strdup(value); - if (!*field) err(EX_OSERR, "strdup"); + if (!*field) err(1, "strdup"); } #define ENUM_TAG \ @@ -273,7 +272,7 @@ static inline void utilPush(struct Util *util, const char *arg) { if (1 + util->argc < UtilCap) { util->argv[util->argc++] = arg; } else { - errx(EX_CONFIG, "too many utility arguments"); + errx(1, "too many utility arguments"); } } diff --git a/command.c b/command.c index 502ff17..9b2b4eb 100644 --- a/command.c +++ b/command.c @@ -516,7 +516,7 @@ static void commandExec(uint id, char *params) { execID = id; pid_t pid = fork(); - if (pid < 0) err(EX_OSERR, "fork"); + if (pid < 0) err(1, "fork"); if (pid) return; setsid(); @@ -527,7 +527,7 @@ static void commandExec(uint id, char *params) { const char *shell = getenv("SHELL") ?: "/bin/sh"; execl(shell, shell, "-c", params, NULL); warn("%s", shell); - _exit(EX_UNAVAILABLE); + _exit(127); } static void commandHelp(uint id, char *params) { @@ -545,7 +545,7 @@ static void commandHelp(uint id, char *params) { uiHide(); pid_t pid = fork(); - if (pid < 0) err(EX_OSERR, "fork"); + if (pid < 0) err(1, "fork"); if (pid) return; char buf[256]; @@ -554,7 +554,7 @@ static void commandHelp(uint id, char *params) { execlp("man", "man", "1", "catgirl", NULL); dup2(utilPipe[1], STDERR_FILENO); warn("man"); - _exit(EX_UNAVAILABLE); + _exit(127); } enum Flag { diff --git a/complete.c b/complete.c index 3552c7c..d7108e6 100644 --- a/complete.c +++ b/complete.c @@ -28,7 +28,6 @@ #include <err.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include "chat.h" @@ -47,10 +46,10 @@ static struct Node *tail; static struct Node *alloc(uint id, const char *str, enum Color color) { struct Node *node = calloc(1, sizeof(*node)); - if (!node) err(EX_OSERR, "calloc"); + if (!node) err(1, "calloc"); node->id = id; node->str = strdup(str); - if (!node->str) err(EX_OSERR, "strdup"); + if (!node->str) err(1, "strdup"); node->color = color; node->bits = 0; return node; @@ -117,7 +116,7 @@ void completeReplace(const char *old, const char *new) { if (strcmp(node->str, old)) continue; free(node->str); node->str = strdup(new); - if (!node->str) err(EX_OSERR, "strdup"); + if (!node->str) err(1, "strdup"); prepend(detach(node)); } } diff --git a/config.c b/config.c index be88f2f..e568e40 100644 --- a/config.c +++ b/config.c @@ -97,13 +97,6 @@ int getopt_config( } char *equal = &name[len] + strspn(&name[len], WS); - if (*equal && *equal != '=') { - warnx( - "%s:%zu: option `%s' missing equals sign", - path, num, option->name - ); - return clean('?'); - } if (option->has_arg == no_argument && *equal) { warnx( "%s:%zu: option `%s' doesn't allow an argument", @@ -121,8 +114,11 @@ int getopt_config( optarg = NULL; if (*equal) { - char *arg = &equal[1] + strspn(&equal[1], WS); - optarg = strdup(arg); + if (*equal == '=') { + optarg = strdup(&equal[1] + strspn(&equal[1], WS)); + } else { + optarg = strdup(equal); + } if (!optarg) { warn("getopt_config"); return clean('?'); diff --git a/configure b/configure index 07e3245..1fd4ad2 100755 --- a/configure +++ b/configure @@ -54,6 +54,13 @@ case "$(uname)" in config libtls ncursesw defvar OPENSSL_BIN openssl exec_prefix /bin/openssl ;; + (NetBSD) + cflags "-D'explicit_bzero(b,l)=explicit_memset((b),0,(l))'" + config libtls ncurses + echo 'LDADD.ncursesw = ${LDADD.ncurses}' + echo 'OBJS += compat_readpassphrase.o' + defstr OPENSSL_BIN /usr/bin/openssl + ;; (*) config libtls ncursesw defvar OPENSSL_BIN openssl exec_prefix /bin/openssl diff --git a/filter.c b/filter.c index a7f9a29..bbe40c8 100644 --- a/filter.c +++ b/filter.c @@ -31,7 +31,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include "chat.h" @@ -48,14 +47,14 @@ struct Filter filterParse(enum Heat heat, char *pattern) { } struct Filter filterAdd(enum Heat heat, const char *pattern) { - if (len == FilterCap) errx(EX_CONFIG, "filter limit exceeded"); + if (len == FilterCap) errx(1, "filter limit exceeded"); char *own; if (!strchr(pattern, '!') && !strchr(pattern, ' ')) { int n = asprintf(&own, "%s!*@*", pattern); - if (n < 0) err(EX_OSERR, "asprintf"); + if (n < 0) err(1, "asprintf"); } else { own = strdup(pattern); - if (!own) err(EX_OSERR, "strdup"); + if (!own) err(1, "strdup"); } struct Filter filter = filterParse(heat, own); filters[len++] = filter; @@ -105,7 +104,7 @@ static void icedPush(const char *msgID) { size_t i = iced.len % IcedCap; free(iced.msgIDs[i]); iced.msgIDs[i] = strdup(msgID); - if (!iced.msgIDs[i]) err(EX_OSERR, "strdup"); + if (!iced.msgIDs[i]) err(1, "strdup"); iced.len++; } diff --git a/handle.c b/handle.c index 5a2cf7c..0cc7c04 100644 --- a/handle.c +++ b/handle.c @@ -32,7 +32,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include <wchar.h> #include "chat.h" @@ -83,7 +82,7 @@ static void require(struct Message *msg, bool origin, uint len) { } for (uint i = 0; i < len; ++i) { if (msg->params[i]) continue; - errx(EX_PROTOCOL, "%s missing parameter %u", msg->cmd, 1 + i); + errx(1, "%s missing parameter %u", msg->cmd, 1 + i); } } @@ -162,7 +161,7 @@ static void handleErrorNicknameInUse(struct Message *msg) { static void handleErrorErroneousNickname(struct Message *msg) { require(msg, false, 3); if (!strcmp(self.nick, "*")) { - errx(EX_CONFIG, "%s: %s", msg->params[1], msg->params[2]); + errx(1, "%s: %s", msg->params[1], msg->params[2]); } else { handleErrorGeneric(msg); } @@ -193,7 +192,7 @@ static void handleCap(struct Message *msg) { } if (!(self.caps & CapSASL)) ircFormat("CAP END\r\n"); } else if (!strcmp(msg->params[1], "NAK")) { - errx(EX_CONFIG, "server does not support %s", msg->params[2]); + errx(1, "server does not support %s", msg->params[2]); } } @@ -237,7 +236,7 @@ static void handleAuthenticate(struct Message *msg) { size_t userLen = strlen(self.plainUser); size_t passLen = strlen(self.plainPass); size_t len = 1 + userLen + 1 + passLen; - if (sizeof(buf) < len) errx(EX_USAGE, "SASL PLAIN is too long"); + if (sizeof(buf) < len) errx(1, "SASL PLAIN is too long"); memcpy(&buf[1], self.plainUser, userLen); memcpy(&buf[1 + userLen + 1], self.plainPass, passLen); @@ -260,7 +259,7 @@ static void handleReplyLoggedIn(struct Message *msg) { static void handleErrorSASLFail(struct Message *msg) { require(msg, false, 2); - errx(EX_CONFIG, "%s", msg->params[1]); + errx(1, "%s", msg->params[1]); } static void handleReplyWelcome(struct Message *msg) { @@ -315,7 +314,7 @@ static void handleReplyISupport(struct Message *msg) { char *modes = strsep(&msg->params[i], ")"); char *prefixes = msg->params[i]; if (!modes || !prefixes || strlen(modes) != strlen(prefixes)) { - errx(EX_PROTOCOL, "invalid PREFIX value"); + errx(1, "invalid PREFIX value"); } set(&network.prefixModes, modes); set(&network.prefixes, prefixes); @@ -325,7 +324,7 @@ static void handleReplyISupport(struct Message *msg) { char *setParam = strsep(&msg->params[i], ","); char *channel = strsep(&msg->params[i], ","); if (!list || !param || !setParam || !channel) { - errx(EX_PROTOCOL, "invalid CHANMODES value"); + errx(1, "invalid CHANMODES value"); } set(&network.listModes, list); set(&network.paramModes, param); @@ -857,7 +856,7 @@ static void handleMode(struct Message *msg) { if (strchr(network.prefixModes, *ch)) { if (i >= ParamCap || !msg->params[i]) { - errx(EX_PROTOCOL, "MODE missing %s parameter", mode); + errx(1, "MODE missing %s parameter", mode); } char *nick = msg->params[i++]; char prefix = network.prefixes[ @@ -884,7 +883,7 @@ static void handleMode(struct Message *msg) { if (strchr(network.listModes, *ch)) { if (i >= ParamCap || !msg->params[i]) { - errx(EX_PROTOCOL, "MODE missing %s parameter", mode); + errx(1, "MODE missing %s parameter", mode); } char *mask = msg->params[i++]; if (*ch == 'b') { @@ -917,7 +916,7 @@ static void handleMode(struct Message *msg) { if (strchr(network.paramModes, *ch)) { if (i >= ParamCap || !msg->params[i]) { - errx(EX_PROTOCOL, "MODE missing %s parameter", mode); + errx(1, "MODE missing %s parameter", mode); } char *param = msg->params[i++]; uiFormat( @@ -934,7 +933,7 @@ static void handleMode(struct Message *msg) { if (strchr(network.setParamModes, *ch) && set) { if (i >= ParamCap || !msg->params[i]) { - errx(EX_PROTOCOL, "MODE missing %s parameter", mode); + errx(1, "MODE missing %s parameter", mode); } char *param = msg->params[i++]; uiFormat( @@ -1337,7 +1336,7 @@ static void handlePing(struct Message *msg) { static void handleError(struct Message *msg) { require(msg, false, 1); - errx(EX_UNAVAILABLE, "%s", msg->params[0]); + errx(69, "%s", msg->params[0]); } static const struct Handler { diff --git a/input.c b/input.c index 6b33b93..7e1f9c1 100644 --- a/input.c +++ b/input.c @@ -35,7 +35,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include <termios.h> #include <unistd.h> #include <wchar.h> @@ -100,7 +99,7 @@ void inputInit(void) { struct termios term; int error = tcgetattr(STDOUT_FILENO, &term); - if (error) err(EX_OSERR, "tcgetattr"); + if (error) err(1, "tcgetattr"); // Gain use of C-q, C-s, C-c, C-z, C-y, C-v, C-o. term.c_iflag &= ~IXON; @@ -113,7 +112,7 @@ void inputInit(void) { term.c_cc[VDISCARD] = _POSIX_VDISABLE; error = tcsetattr(STDOUT_FILENO, TCSANOW, &term); - if (error) err(EX_OSERR, "tcsetattr"); + if (error) err(1, "tcsetattr"); def_prog_mode(); @@ -172,7 +171,7 @@ void inputUpdate(void) { size_t pos = 0; const char *ptr = editString(&edits[id], &buf, &cap, &pos); - if (!ptr) err(EX_OSERR, "editString"); + if (!ptr) err(1, "editString"); const char *prefix = ""; const char *prompt = self.nick; @@ -394,7 +393,7 @@ fail: static void inputEnter(void) { uint id = windowID(); char *cmd = editString(&edits[id], &buf, &cap, NULL); - if (!cmd) err(EX_OSERR, "editString"); + if (!cmd) err(1, "editString"); tabAccept(); editFn(&edits[id], EditClear); @@ -450,7 +449,7 @@ static void keyCode(int code) { break; case KEY_SHOME: windowScroll(ScrollAll, +1); break; case KEY_UP: windowScroll(ScrollOne, +1); } - if (error) err(EX_OSERR, "editFn"); + if (error) err(1, "editFn"); } static void keyCtrl(wchar_t ch) { @@ -480,7 +479,7 @@ static void keyCtrl(wchar_t ch) { break; case L'X': error = macroExpand(edit); tabAccept(); break; case L'Y': error = editFn(edit, EditPaste); } - if (error) err(EX_OSERR, "editFn"); + if (error) err(1, "editFn"); } static void keyStyle(wchar_t ch) { @@ -516,7 +515,7 @@ static void keyStyle(wchar_t ch) { struct Edit *edit = &edits[windowID()]; for (char *ch = buf; *ch; ++ch) { int error = editInsert(edit, *ch); - if (error) err(EX_OSERR, "editInsert"); + if (error) err(1, "editInsert"); } } @@ -552,7 +551,7 @@ void inputRead(void) { paste ^= true; } else if (paste || literal) { int error = editInsert(&edits[windowID()], ch); - if (error) err(EX_OSERR, "editInsert"); + if (error) err(1, "editInsert"); } else if (ret == KEY_CODE_YES) { keyCode(ch); } else if (ch == (L'Z' ^ L'@')) { @@ -568,7 +567,7 @@ void inputRead(void) { keyCtrl(ch); } else { int error = editInsert(&edits[windowID()], ch); - if (error) err(EX_OSERR, "editInsert"); + if (error) err(1, "editInsert"); } style = false; literal = false; @@ -609,7 +608,7 @@ int inputSave(FILE *file) { static ssize_t readString(FILE *file, char **buf, size_t *cap) { ssize_t len = getdelim(buf, cap, '\0', file); - if (len < 0 && !feof(file)) err(EX_IOERR, "getdelim"); + if (len < 0 && !feof(file)) err(1, "getdelim"); return len; } @@ -620,7 +619,7 @@ void inputLoad(FILE *file, size_t version) { readString(file, &buf, &cap); size_t max = strlen(buf); int error = editReserve(&edits[id], 0, max); - if (error) err(EX_OSERR, "editReserve"); + if (error) err(1, "editReserve"); size_t len = mbstowcs(edits[id].buf, buf, max); assert(len != (size_t)-1); edits[id].len = len; diff --git a/irc.c b/irc.c index 1fc2c3f..28e557b 100644 --- a/irc.c +++ b/irc.c @@ -38,7 +38,6 @@ #include <string.h> #include <sys/socket.h> #include <sys/stat.h> -#include <sysexits.h> #include <tls.h> #include <unistd.h> @@ -54,7 +53,7 @@ void ircConfig( char buf[PATH_MAX]; config = tls_config_new(); - if (!config) errx(EX_SOFTWARE, "tls_config_new"); + if (!config) errx(1, "tls_config_new"); if (insecure) { tls_config_insecure_noverifycert(config); @@ -66,7 +65,7 @@ void ircConfig( error = tls_config_set_ca_file(config, buf); if (!error) break; } - if (error) errx(EX_NOINPUT, "%s: %s", trust, tls_config_error(config)); + if (error) errx(1, "%s: %s", trust, tls_config_error(config)); } // Explicitly load the default CA cert file on OpenBSD now so it doesn't @@ -76,7 +75,7 @@ void ircConfig( if (!insecure && !trust) { const char *ca = tls_default_ca_cert_file(); error = tls_config_set_ca_file(config, ca); - if (error) errx(EX_OSFILE, "%s: %s", ca, tls_config_error(config)); + if (error) errx(1, "%s: %s", ca, tls_config_error(config)); } #endif @@ -89,21 +88,21 @@ void ircConfig( } if (!error) break; } - if (error) errx(EX_NOINPUT, "%s: %s", cert, tls_config_error(config)); + if (error) errx(1, "%s: %s", cert, tls_config_error(config)); } if (priv) { for (int i = 0; configPath(buf, sizeof(buf), priv, i); ++i) { error = tls_config_set_key_file(config, buf); if (!error) break; } - if (error) errx(EX_NOINPUT, "%s: %s", priv, tls_config_error(config)); + if (error) errx(1, "%s: %s", priv, tls_config_error(config)); } client = tls_client(); - if (!client) errx(EX_SOFTWARE, "tls_client"); + if (!client) errx(1, "tls_client"); error = tls_configure(client, config); - if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client)); + if (error) errx(1, "tls_configure: %s", tls_error(client)); } int ircConnect(const char *bindHost, const char *host, const char *port) { @@ -120,11 +119,11 @@ int ircConnect(const char *bindHost, const char *host, const char *port) { if (bindHost) { error = getaddrinfo(bindHost, NULL, &hints, &head); - if (error) errx(EX_NOHOST, "%s: %s", bindHost, gai_strerror(error)); + if (error) errx(1, "%s: %s", bindHost, gai_strerror(error)); for (struct addrinfo *ai = head; ai; ai = ai->ai_next) { sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sock < 0) err(EX_OSERR, "socket"); + if (sock < 0) err(1, "socket"); error = bind(sock, ai->ai_addr, ai->ai_addrlen); if (!error) { @@ -135,17 +134,17 @@ int ircConnect(const char *bindHost, const char *host, const char *port) { close(sock); sock = -1; } - if (sock < 0) err(EX_UNAVAILABLE, "%s", bindHost); + if (sock < 0) err(1, "%s", bindHost); freeaddrinfo(head); } error = getaddrinfo(host, port, &hints, &head); - if (error) errx(EX_NOHOST, "%s:%s: %s", host, port, gai_strerror(error)); + if (error) errx(1, "%s:%s: %s", host, port, gai_strerror(error)); for (struct addrinfo *ai = head; ai; ai = ai->ai_next) { if (sock < 0) { sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sock < 0) err(EX_OSERR, "socket"); + if (sock < 0) err(1, "socket"); } error = connect(sock, ai->ai_addr, ai->ai_addrlen); @@ -155,12 +154,12 @@ int ircConnect(const char *bindHost, const char *host, const char *port) { close(sock); sock = -1; } - if (sock < 0) err(EX_UNAVAILABLE, "%s:%s", host, port); + if (sock < 0) err(69, "%s:%s", host, port); freeaddrinfo(head); fcntl(sock, F_SETFD, FD_CLOEXEC); error = tls_connect_socket(client, sock, host); - if (error) errx(EX_PROTOCOL, "tls_connect: %s", tls_error(client)); + if (error) errx(1, "tls_connect: %s", tls_error(client)); return sock; } @@ -170,7 +169,7 @@ void ircHandshake(void) { do { error = tls_handshake(client); } while (error == TLS_WANT_POLLIN || error == TLS_WANT_POLLOUT); - if (error) errx(EX_PROTOCOL, "tls_handshake: %s", tls_error(client)); + if (error) errx(1, "tls_handshake: %s", tls_error(client)); tls_config_clear_keys(config); } @@ -202,7 +201,7 @@ void ircSend(const char *ptr, size_t len) { while (len) { ssize_t ret = tls_write(client, ptr, len); if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue; - if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(client)); + if (ret < 0) errx(1, "tls_write: %s", tls_error(client)); ptr += ret; len -= ret; } @@ -287,8 +286,8 @@ void ircRecv(void) { assert(client); ssize_t ret = tls_read(client, &buf[len], sizeof(buf) - len); if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) return; - if (ret < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client)); - if (!ret) errx(EX_PROTOCOL, "server closed connection"); + if (ret < 0) errx(1, "tls_read: %s", tls_error(client)); + if (!ret) errx(69, "server closed connection"); len += ret; char *crlf; diff --git a/log.c b/log.c index d6b3f2a..181c009 100644 --- a/log.c +++ b/log.c @@ -34,7 +34,6 @@ #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> -#include <sysexits.h> #include <time.h> #include <unistd.h> @@ -49,13 +48,13 @@ static int logDir = -1; void logOpen(void) { char buf[PATH_MAX]; int error = mkdir(dataPath(buf, sizeof(buf), "", 0), S_IRWXU); - if (error && errno != EEXIST) err(EX_CANTCREAT, "%s", buf); + if (error && errno != EEXIST) err(1, "%s", buf); error = mkdir(dataPath(buf, sizeof(buf), "log", 0), S_IRWXU); - if (error && errno != EEXIST) err(EX_CANTCREAT, "%s", buf); + if (error && errno != EEXIST) err(1, "%s", buf); logDir = open(buf, O_RDONLY | O_CLOEXEC); - if (logDir < 0) err(EX_CANTCREAT, "%s", buf); + if (logDir < 0) err(1, "%s", buf); #ifdef __FreeBSD__ cap_rights_t rights; @@ -64,13 +63,13 @@ void logOpen(void) { /* for fdopen(3) */ CAP_FCNTL, CAP_FSTAT ); error = caph_rights_limit(logDir, &rights); - if (error) err(EX_OSERR, "cap_rights_limit"); + if (error) err(1, "cap_rights_limit"); #endif } static void logMkdir(const char *path) { int error = mkdirat(logDir, path, S_IRWXU); - if (error && errno != EEXIST) err(EX_CANTCREAT, "log/%s", path); + if (error && errno != EEXIST) err(1, "log/%s", path); } static void sanitize(char *ptr, char *end) { @@ -99,7 +98,7 @@ static FILE *logFile(uint id, const struct tm *tm) { if (logs[id].file) { int error = fclose(logs[id].file); - if (error) err(EX_IOERR, "%s", idNames[id]); + if (error) err(1, "%s", idNames[id]); } logs[id].year = tm->tm_year; @@ -119,16 +118,16 @@ static FILE *logFile(uint id, const struct tm *tm) { logMkdir(path); size_t len = strftime(ptr, end - ptr, "/%F.log", tm); - if (!len) errx(EX_CANTCREAT, "log path too long"); + if (!len) errx(1, "log path too long"); int fd = openat( logDir, path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR ); - if (fd < 0) err(EX_CANTCREAT, "log/%s", path); + if (fd < 0) err(1, "log/%s", path); logs[id].file = fdopen(fd, "a"); - if (!logs[id].file) err(EX_OSERR, "fdopen"); + if (!logs[id].file) err(1, "fdopen"); setlinebuf(logs[id].file); return logs[id].file; @@ -139,7 +138,7 @@ void logClose(void) { for (uint id = 0; id < IDCap; ++id) { if (!logs[id].file) continue; int error = fclose(logs[id].file); - if (error) err(EX_IOERR, "%s", idNames[id]); + if (error) err(1, "%s", idNames[id]); } close(logDir); } @@ -149,21 +148,21 @@ void logFormat(uint id, const time_t *src, const char *format, ...) { time_t ts = (src ? *src : time(NULL)); struct tm *tm = localtime(&ts); - if (!tm) err(EX_OSERR, "localtime"); + if (!tm) err(1, "localtime"); FILE *file = logFile(id, tm); char buf[sizeof("0000-00-00T00:00:00+0000")]; strftime(buf, sizeof(buf), "%FT%T%z", tm); int n = fprintf(file, "[%s] ", buf); - if (n < 0) err(EX_IOERR, "%s", idNames[id]); + if (n < 0) err(1, "%s", idNames[id]); va_list ap; va_start(ap, format); n = vfprintf(file, format, ap); va_end(ap); - if (n < 0) err(EX_IOERR, "%s", idNames[id]); + if (n < 0) err(1, "%s", idNames[id]); n = fprintf(file, "\n"); - if (n < 0) err(EX_IOERR, "%s", idNames[id]); + if (n < 0) err(1, "%s", idNames[id]); } diff --git a/sandman.m b/sandman.m index 2e5c4db..c9d0705 100644 --- a/sandman.m +++ b/sandman.m @@ -19,7 +19,6 @@ #import <signal.h> #import <stdio.h> #import <stdlib.h> -#import <sysexits.h> #import <unistd.h> typedef unsigned uint; @@ -27,17 +26,17 @@ typedef unsigned uint; static pid_t pid; static void spawn(char *argv[]) { pid = fork(); - if (pid < 0) err(EX_OSERR, "fork"); + if (pid < 0) err(1, "fork"); if (pid) return; execvp(argv[0], argv); - err(EX_CONFIG, "%s", argv[0]); + err(127, "%s", argv[0]); } static void handler(int signal) { (void)signal; int status; pid_t pid = wait(&status); - if (pid < 0) _exit(EX_OSERR); + if (pid < 0) _exit(1); _exit(status); } @@ -47,12 +46,12 @@ int main(int argc, char *argv[]) { for (int opt; 0 < (opt = getopt(argc, argv, "t:"));) { switch (opt) { break; case 't': delay = strtoul(optarg, NULL, 10); - break; default: return EX_USAGE; + break; default: return 1; } } argc -= optind; argv += optind; - if (!argc) errx(EX_USAGE, "command required"); + if (!argc) errx(1, "command required"); NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; NSNotificationCenter *notifCenter = [workspace notificationCenter]; @@ -64,7 +63,7 @@ int main(int argc, char *argv[]) { (void)notif; signal(SIGCHLD, SIG_IGN); int error = kill(pid, SIGHUP); - if (error) err(EX_UNAVAILABLE, "kill"); + if (error) err(1, "kill"); int status; wait(&status); }]; diff --git a/scripts/build-chroot.sh b/scripts/build-chroot.sh deleted file mode 100644 index a0fcf32..0000000 --- a/scripts/build-chroot.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/sh -set -eux - -CHROOT_USER=$1 -CHROOT_GROUP=$2 - -if [ "$(uname)" = 'OpenBSD' ]; then - install -d -o root -g wheel \ - root \ - root/bin \ - root/etc/ssl \ - root/home \ - root/usr/bin \ - root/usr/lib \ - root/usr/libexec \ - root/usr/share/man - install -d -o ${CHROOT_USER} -g ${CHROOT_GROUP} \ - root/home/${CHROOT_USER} \ - root/home/${CHROOT_USER}/.local/share - - cp -fp /bin/sh root/bin - cp -fp /usr/libexec/ld.so root/usr/libexec - export LD_TRACE_LOADED_OBJECTS_FMT1='%p\n' - export LD_TRACE_LOADED_OBJECTS_FMT2='' - for bin in ./catgirl /usr/bin/mandoc /usr/bin/less; do - LD_TRACE_LOADED_OBJECTS=1 $bin | xargs -t -J % cp -fp % root/usr/lib - done - cp -fp /usr/bin/printf /usr/bin/mandoc /usr/bin/less root/usr/bin - make install DESTDIR=root PREFIX=/usr MANDIR=/usr/share/man - install scripts/chroot-prompt.sh root/usr/bin/catgirl-prompt - install scripts/chroot-man.sh root/usr/bin/man - - cp -fp /etc/hosts /etc/resolv.conf root/etc - cp -fp /etc/ssl/cert.pem root/etc/ssl - cp -af /usr/share/locale /usr/share/terminfo root/usr/share - - tar -c -f chroot.tar -C root bin etc home usr - -elif [ "$(uname)" = 'FreeBSD' ]; then - install -d -o root -g wheel \ - root \ - root/bin \ - root/etc \ - root/home \ - root/lib \ - root/libexec \ - root/usr/bin \ - root/usr/local/etc/ssl \ - root/usr/share/man \ - root/usr/share/misc - install -d -o ${CHROOT_USER} -g ${CHROOT_GROUP} \ - root/home/${CHROOT_USER} \ - root/home/${CHROOT_USER}/.local/share - - cp -fp /libexec/ld-elf.so.1 root/libexec - ldd -f '%p\n' catgirl /usr/bin/mandoc /usr/bin/less \ - | sort -u | xargs -t -J % cp -fp % root/lib - chflags noschg root/libexec/* root/lib/* - cp -fp /rescue/sh /usr/bin/mandoc /usr/bin/less root/bin - make install DESTDIR=root PREFIX=/usr MANDIR=/usr/share/man - install scripts/chroot-prompt.sh root/usr/bin/catgirl-prompt - install scripts/chroot-man.sh root/usr/bin/man - - cp -fp /etc/hosts /etc/resolv.conf root/etc - cp -fp /usr/local/etc/ssl/cert.pem root/usr/local/etc/ssl - cp -af /usr/share/locale root/usr/share - cp -fp /usr/share/misc/termcap.db root/usr/share/misc - - tar -c -f chroot.tar -C root bin etc home lib libexec usr - -else - echo "Don't know how to build chroot on $(uname)" >&2 - exit 1 -fi diff --git a/scripts/chroot-man.sh b/scripts/chroot-man.sh deleted file mode 100644 index 9d686f9..0000000 --- a/scripts/chroot-man.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec mandoc /usr/share/man/man1/catgirl.1 | LESSSECURE=1 less diff --git a/scripts/chroot-prompt.sh b/scripts/chroot-prompt.sh deleted file mode 100644 index 2b34426..0000000 --- a/scripts/chroot-prompt.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -set -eu - -printf 'Name: ' -read -r nick rest -printf '%s %s\n' "$nick" "$SSH_CLIENT" >>nicks.log -exec catgirl -K -n "$nick" -s "${nick##*/}" -u "${SSH_CLIENT%% *}" "$@" diff --git a/scripts/reconnect.sh b/scripts/reconnect.sh new file mode 100644 index 0000000..92d9668 --- /dev/null +++ b/scripts/reconnect.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -u + +while :; do + catgirl "$@" + status=$? + if [ $status -ne 69 ]; then + exit $status + fi +done diff --git a/scripts/sshd_config b/scripts/sshd_config deleted file mode 100644 index c7e99ec..0000000 --- a/scripts/sshd_config +++ /dev/null @@ -1,9 +0,0 @@ -UsePAM no - -Match User chat - PasswordAuthentication yes - PermitEmptyPasswords yes - ChrootDirectory /home/chat - ForceCommand catgirl-prompt - DisableForwarding yes - MaxSessions 1 diff --git a/ui.c b/ui.c index 079ee19..df675b1 100644 --- a/ui.c +++ b/ui.c @@ -39,7 +39,6 @@ #include <stdlib.h> #include <string.h> #include <sys/file.h> -#include <sysexits.h> #include <term.h> #include <time.h> #include <unistd.h> @@ -122,13 +121,13 @@ void uiInit(void) { } uiStatus = newwin(StatusLines, COLS, 0, 0); - if (!uiStatus) err(EX_OSERR, "newwin"); + if (!uiStatus) err(1, "newwin"); uiMain = newwin(MAIN_LINES, COLS, StatusLines, 0); - if (!uiMain) err(EX_OSERR, "newwin"); + if (!uiMain) err(1, "newwin"); uiInput = newpad(InputLines, InputCols); - if (!uiInput) err(EX_OSERR, "newpad"); + if (!uiInput) err(1, "newpad"); windowInit(); uiShow(); @@ -240,7 +239,7 @@ static void notify(uint id, const char *str) { utilPush(&util, buf); pid_t pid = fork(); - if (pid < 0) err(EX_OSERR, "fork"); + if (pid < 0) err(1, "fork"); if (pid) return; setsid(); @@ -249,7 +248,7 @@ static void notify(uint id, const char *str) { dup2(utilPipe[1], STDERR_FILENO); execvp(util.argv[0], (char *const *)util.argv); warn("%s", util.argv[0]); - _exit(EX_CONFIG); + _exit(127); } void uiWrite(uint id, enum Heat heat, const time_t *src, const char *str) { @@ -296,7 +295,7 @@ static size_t signatureVersion(uint64_t signature) { for (size_t i = 0; i < ARRAY_LEN(Signatures); ++i) { if (signature == Signatures[i]) return i; } - errx(EX_DATAERR, "unknown save file signature %" PRIX64, signature); + errx(1, "unknown save file signature %" PRIX64, signature); } static int writeUint64(FILE *file, uint64_t u) { @@ -317,32 +316,32 @@ int uiSave(void) { static uint64_t readUint64(FILE *file) { uint64_t u; fread(&u, sizeof(u), 1, file); - if (ferror(file)) err(EX_IOERR, "fread"); - if (feof(file)) errx(EX_DATAERR, "unexpected end of save file"); + if (ferror(file)) err(1, "fread"); + if (feof(file)) errx(1, "unexpected end of save file"); return u; } void uiLoad(const char *name) { int error; saveFile = dataOpen(name, "a+e"); - if (!saveFile) exit(EX_CANTCREAT); + if (!saveFile) exit(1); rewind(saveFile); #ifdef __FreeBSD__ cap_rights_t rights; cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_FLOCK, CAP_FTRUNCATE); error = caph_rights_limit(fileno(saveFile), &rights); - if (error) err(EX_OSERR, "cap_rights_limit"); + if (error) err(1, "cap_rights_limit"); #endif error = flock(fileno(saveFile), LOCK_EX | LOCK_NB); if (error && errno == EWOULDBLOCK) { - errx(EX_CANTCREAT, "%s: save file in use", name); + errx(1, "%s: save file in use", name); } time_t signature; fread(&signature, sizeof(signature), 1, saveFile); - if (ferror(saveFile)) err(EX_IOERR, "fread"); + if (ferror(saveFile)) err(1, "fread"); if (feof(saveFile)) { return; } diff --git a/url.c b/url.c index 7da0968..349dc00 100644 --- a/url.c +++ b/url.c @@ -32,7 +32,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include <unistd.h> #include "chat.h" @@ -67,7 +66,7 @@ static void compile(void) { if (!error) return; char buf[256]; regerror(error, &Regex, buf, sizeof(buf)); - errx(EX_SOFTWARE, "regcomp: %s: %s", buf, Pattern); + errx(1, "regcomp: %s: %s", buf, Pattern); } struct URL { @@ -92,10 +91,10 @@ static void push(uint id, const char *nick, const char *str, size_t len) { url->nick = NULL; if (nick) { url->nick = strdup(nick); - if (!url->nick) err(EX_OSERR, "strdup"); + if (!url->nick) err(1, "strdup"); } url->url = malloc(len + 1); - if (!url->url) err(EX_OSERR, "malloc"); + if (!url->url) err(1, "malloc"); char buf[1024]; snprintf(buf, sizeof(buf), "%.*s", (int)len, str); @@ -120,7 +119,7 @@ static const struct Util OpenUtils[] = { static void urlOpen(const char *url) { pid_t pid = fork(); - if (pid < 0) err(EX_OSERR, "fork"); + if (pid < 0) err(1, "fork"); if (pid) return; setsid(); @@ -132,7 +131,7 @@ static void urlOpen(const char *url) { utilPush(&util, url); execvp(util.argv[0], (char *const *)util.argv); warn("%s", util.argv[0]); - _exit(EX_CONFIG); + _exit(127); } for (size_t i = 0; i < ARRAY_LEN(OpenUtils); ++i) { struct Util util = OpenUtils[i]; @@ -140,11 +139,11 @@ static void urlOpen(const char *url) { execvp(util.argv[0], (char *const *)util.argv); if (errno != ENOENT) { warn("%s", util.argv[0]); - _exit(EX_CONFIG); + _exit(127); } } warnx("no open utility found"); - _exit(EX_CONFIG); + _exit(127); } struct Util urlCopyUtil; @@ -158,18 +157,18 @@ static const struct Util CopyUtils[] = { static void urlCopy(const char *url) { int rw[2]; int error = pipe(rw); - if (error) err(EX_OSERR, "pipe"); + if (error) err(1, "pipe"); size_t len = strlen(url); if (len > PIPE_BUF) len = PIPE_BUF; ssize_t n = write(rw[1], url, len); - if (n < 0) err(EX_IOERR, "write"); + if (n < 0) err(1, "write"); error = close(rw[1]); - if (error) err(EX_IOERR, "close"); + if (error) err(1, "close"); pid_t pid = fork(); - if (pid < 0) err(EX_OSERR, "fork"); + if (pid < 0) err(1, "fork"); if (pid) { close(rw[0]); return; @@ -183,17 +182,17 @@ static void urlCopy(const char *url) { if (urlCopyUtil.argc) { execvp(urlCopyUtil.argv[0], (char *const *)urlCopyUtil.argv); warn("%s", urlCopyUtil.argv[0]); - _exit(EX_CONFIG); + _exit(127); } for (size_t i = 0; i < ARRAY_LEN(CopyUtils); ++i) { execvp(CopyUtils[i].argv[0], (char *const *)CopyUtils[i].argv); if (errno != ENOENT) { warn("%s", CopyUtils[i].argv[0]); - _exit(EX_CONFIG); + _exit(127); } } warnx("no copy utility found"); - _exit(EX_CONFIG); + _exit(127); } void urlOpenCount(uint id, uint count) { @@ -239,7 +238,7 @@ static int writeString(FILE *file, const char *str) { } static ssize_t readString(FILE *file, char **buf, size_t *cap) { ssize_t len = getdelim(buf, cap, '\0', file); - if (len < 0 && !feof(file)) err(EX_IOERR, "getdelim"); + if (len < 0 && !feof(file)) err(1, "getdelim"); return len; } @@ -269,11 +268,11 @@ void urlLoad(FILE *file, size_t version) { readString(file, &buf, &cap); if (buf[0]) { url->nick = strdup(buf); - if (!url->nick) err(EX_OSERR, "strdup"); + if (!url->nick) err(1, "strdup"); } readString(file, &buf, &cap); url->url = strdup(buf); - if (!url->url) err(EX_OSERR, "strdup"); + if (!url->url) err(1, "strdup"); } free(buf); } diff --git a/window.c b/window.c index 00041aa..2e79a65 100644 --- a/window.c +++ b/window.c @@ -35,7 +35,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include <time.h> #include "chat.h" @@ -107,7 +106,7 @@ uint windowFor(uint id) { } struct Window *window = calloc(1, sizeof(*window)); - if (!window) err(EX_OSERR, "malloc"); + if (!window) err(1, "malloc"); window->id = id; window->mark = true; @@ -132,7 +131,7 @@ void windowInit(void) { struct tm *time = localtime(&(time_t) { -22100400 }); size_t len = strftime(buf, sizeof(buf), fmt, time); - if (!len) errx(EX_CONFIG, "invalid timestamp format: %s", fmt); + if (!len) errx(1, "invalid timestamp format: %s", fmt); int y; waddstr(uiMain, buf); @@ -147,6 +146,7 @@ static int styleAdd(WINDOW *win, struct Style init, const char *str) { struct Style style = init; while (*str) { size_t len = styleParse(&style, &str); + if (!len) continue; wattr_set(win, uiAttr(style), uiPair(style), NULL); if (waddnstr(win, str, len) == ERR) return -1; @@ -621,14 +621,14 @@ int windowSave(FILE *file) { static time_t readTime(FILE *file) { time_t time; fread(&time, sizeof(time), 1, file); - if (ferror(file)) err(EX_IOERR, "fread"); - if (feof(file)) errx(EX_DATAERR, "unexpected end of save file"); + if (ferror(file)) err(1, "fread"); + if (feof(file)) errx(1, "unexpected end of save file"); return time; } static ssize_t readString(FILE *file, char **buf, size_t *cap) { ssize_t len = getdelim(buf, cap, '\0', file); - if (len < 0 && !feof(file)) err(EX_IOERR, "getdelim"); + if (len < 0 && !feof(file)) err(1, "getdelim"); return len; } diff --git a/xdg.c b/xdg.c index 75ee871..6f61cf9 100644 --- a/xdg.c +++ b/xdg.c @@ -32,7 +32,6 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> -#include <sysexits.h> #include "chat.h" @@ -90,7 +89,7 @@ static char *basePath( } else if (home) { snprintf(buf, cap, "%s/%s/" SUBDIR "/%s", home, base.defHome, path); } else { - errx(EX_USAGE, "HOME unset"); + errx(1, "HOME unset"); } return buf; } |