yaic

yet another ii client
git clone git://git.ckyln.com/~cem/yaic.git
Log | Files | Refs | README | LICENSE

yaic (10856B)


      1 #!/bin/sh
      2 
      3 # yaic: Yet another ii client.
      4 # Cem Keylan
      5 
      6 msg() { printf '\033[1m%s\033[m\n' "$@" >/dev/tty ;}
      7 die() { printf '\033[1;38;41merr: %s\033[m\n' "$@" >&2; exit 1;}
      8 
      9 
     10 toggle() {
     11     # A hacky way to create and remove variables
     12     eval "[ \"\$$1\" ] && unset $1 && return 0 ||:"
     13     eval "$1=1"
     14 }
     15 
     16 usage() { printf '%s\n' "usage: ${0##*/} [-s server] [-n nick] [-c chan] [-r dir]" \
     17     "            [-h size] [-l length] [f fold]" "" \
     18     "  -s   Sets the server       (default: $s)" \
     19     "  -n   Sets the nick         (default: $n)" \
     20     "  -c   Sets the channel      (default: $c)" \
     21     "  -r   Sets root irc dir     (default: $r)" \
     22     "  -h   Sets hist value       (default: $h)" \
     23     "  -l   Limits nick space     (default: $l)" \
     24     "  -f   Sets the fold value   (default: $f)" ""
     25     exit 1
     26 }
     27 
     28 # Function for bookmarking a location in the chat.
     29 mark() { printf '%s --- ---------------------------------------------------\n' \
     30                 "$(date +%s)" >> "$outfile" ;}
     31 
     32 gethelp() {
     33     # This is the helper function called
     34     # by :h|:help
     35     msg \
     36         "List of Commands" \
     37         ":b                  Toggles the upper bar" \
     38         ":c|:chan            Prints a list of open channels" \
     39         ":c|:chan [channel]  Changes the channel to the given channel" \
     40         ":h|:help            Prints this help information" \
     41         ":hist               Changes the history size" \
     42         ":l [length]         Change the limit for nick space" \
     43         ":m                  Marks the current position" \
     44         ":n [note]           Adds a note to the current position" \
     45         ":s                  Toggles server notifications" \
     46         ":t                  Toggles outputting your own messages" \
     47         ":q                  Exits the program (not ii)" \
     48         ":x                  Marks the current position and exits" \
     49         ":v|:version         Prints the version"
     50 }
     51 
     52 getchan() {
     53     # Gets a list of all active channels in the server
     54     # Does not include the server itself
     55     msg "Channel list"
     56     for chan in "$r/$s/"* ; do
     57         # We don't want this to return errors.
     58         # shellcheck disable=2015
     59         [ -p "$chan/in" ] && printf '%s\n' "${chan##*/}" >/dev/tty ||:
     60     done
     61 }
     62 
     63 checkchan() {
     64     # Checks if the given channel is open.
     65     [ -p "$r/$s/$1/in" ] || {
     66         msg "Channel '$1' doesn't seem to be open"
     67         return 1
     68     }
     69 }
     70 
     71 changechan() {
     72     # Kill the tail from the previous channel
     73     kill "$tailpid" ||:
     74 
     75     # Restore the original stty configuration
     76     # and reset the colour.
     77     stty "$prestty" ; printf '\033[m'
     78 
     79     # unset the CHAN variable so that it doesn't
     80     # try to change the channel when the user
     81     # is trying to quit.
     82     unset CHAN
     83 
     84     # Run the main function with the new channel
     85     main -s "$s" -n "$n" -c "$1" -r "$r" -h "$h" -l "$l" -f "$f"
     86 }
     87 
     88 statusbar() {
     89     # If NOBAR variable is set, doesn't add a statusbar line.
     90     [ -z "$NOBAR" ] || return 0
     91 
     92     # Get the channel name to a variable. This is done to remove
     93     # '/.' if it's the main server output.
     94     chan="${c#.}"
     95 
     96     # This horrible looking printf function does the following,
     97     # \033[s         - Saves the current position
     98     # \033[H         - Goes to the top of the screen
     99     # \033[K         - Removes that line
    100     # \033[1;37;40m  - Changes colour to black background and white foreground
    101     # "$s/$c"        - Prints the channel name
    102     # \033[u         - Returns back to the previous cursor position
    103     # \033[m         - Restores the colour
    104     printf '\033[s\033[H\033[K\033[1;37;40m%s\033[u\033[m' "$ss${chan:+/}$chan" >/dev/tty
    105 }
    106 
    107 main() {
    108     # Set default values.
    109     : "${f:=60}"  "${n:=${USER:-user}}"
    110     : "${c:=""}"  "${r:=$HOME/irc}"
    111     : "${h:=30}"  "${l:=10}"
    112     : "${s:=irc.freenode.net}" 
    113 
    114     case "$1" in --help|help) usage ; esac
    115 
    116     # Parse all arguments.
    117     while getopts ':s:n:c:r:h:l:f:' opt ; do
    118         case "$opt" in
    119             s) s="$OPTARG" ;;  n) n="$OPTARG" ;;
    120             c) c="$OPTARG" ;;  r) r="$OPTARG" ;;
    121             h) h="$OPTARG" ;;  l) l="$OPTARG" ;;
    122             f) f="$OPTARG" ;;
    123             :) die "'-$OPTARG' requires a value" ;;
    124             ?) die "${0##*/}: invalid option '-$OPTARG'" ;;
    125         esac
    126     done
    127     
    128     # Save in file and out file in a variable.
    129     infile="$r/$s/$c/in"
    130     outfile="$r/$s/$c/out"
    131 
    132     # Save the initial stty state in a variable
    133     # to make sure it doesn't get overriden when
    134     # changing channels.
    135     prestty="$(stty -g)"
    136 
    137     # Check if we are using sbase date, which parses
    138     # Unix epoch without an '@' sign. We don't want
    139     # this to return errors.
    140     # shellcheck disable=2015
    141     date -d @0 >/dev/null 2>&1 && GNUDATE=1 ||:
    142 
    143     # Save a short name of the server.
    144     # This will be used for server messages,
    145     # announcements, etc.
    146     ss=${s%.*} ss=${ss#*.}
    147 
    148     # Check for the existence of in and out files.
    149     # Exit if in file doesn't exist.
    150     # Create it if out file doesn't exist.
    151     [ -p "$infile"  ] || die "in file could not be found, is the daemon available?"
    152     [ -e "$outfile" ] || touch "$outfile"
    153 
    154     # Clear the screen for prettier output
    155     clear
    156 
    157     # Start tail with the history variable. We read the input in
    158     # 3 different variables. First word is always the date. The
    159     # second word is usually the author's nickname, and the rest
    160     # of the line is the message. We will deal with the exceptions
    161     # in the function itself.
    162     tail -f -n "$h" "$outfile" | while read -r date auth msg; do
    163         # Restore colour.
    164         printf '\033[m'
    165 
    166         # Get the time in a pretty format. The date is in Epoch
    167         # format. We convert it to [12:34] format.
    168         cleardate="$(date -d "${GNUDATE:+@}$date" '+[%H:%M]')"
    169 
    170         # Nicknames are in <nick> format and server notifications
    171         # are in -!- format. If we are not dealing with them, then
    172         # it is a server message (not a server notification). We
    173         # need to seperate them in a hacky way.
    174         case "$auth" in \<*\>|-!-|---) ;; *) msg="$auth $msg" auth="$ss" ; esac
    175 
    176         # Don't print server messages if NOSERVER is set.
    177         case "$auth" in \<*\>|---);; *) [ "$NOSERVER" ] && continue ; esac
    178 
    179         # Remove the <*> from nicks.
    180         auth="${auth%>}"; auth="${auth#<}"
    181 
    182         # If the nickname is the user's, print the nick in yellow.
    183         # Other nicknames are printed in blue.
    184         # Server messages are printed grey to be not distracting.
    185         case "$auth" in
    186             "$n")  printf '%s \033[1;33m%-*.*s \033[m' "$cleardate" "$l" "$l" "$auth" ;;
    187             "NOTE")printf '%s \033[1;31m%-*.*s \033[m' "$cleardate" "$l" "$l" "$auth" ;;
    188             "-!-") printf '\033[37m%s %-*.*s ' "$cleardate" "$l" "$l" "$auth";        ;;
    189             "---") printf '\033[37m%s %-*.*s ' "$cleardate" "$l" "$l" "$auth";        ;;
    190             "$ss") printf '%s %-*.*s '         "$cleardate" "$l" "$l" "$auth"         ;;
    191             *)     printf '%s \033[1;36m%-*.*s \033[m' "$cleardate" "$l" "$l" "$auth"
    192                    highlight=1
    193                    ;;
    194         esac
    195 
    196         # This prints the message and folds it. It doesn't add any
    197         # space if it is the first line. But adds space equal to
    198         # the space given for the time, nick, and two spaces. If the
    199         # user's nick is inside the message string, highlight it, but
    200         # only once. We get the highlight value only for messages from
    201         # other users (i.e. not server messages or your own messages).
    202         firstline=1
    203         printf '%s\n' "$msg" | sed "s/$n/${highlight:+}$n${highlight:+}/" | fold -sw "$f" | while read -r line; do
    204             [ "$firstline" ] || printf '%*s' "$(( ${#cleardate} + l + 2 ))" ''
    205             printf '%s\n' "$line" ; unset firstline
    206         done
    207 
    208         # Unset the highlight variable so that we don't highlight
    209         # server messages.
    210         unset highlight
    211 
    212         # Refresh the statusbar line.
    213         statusbar
    214 
    215         # Restore colour.
    216         printf '\033[m'
    217     done >/dev/tty &
    218 
    219     # Save the process id of tail so that we don't have any
    220     # issues when we are trying to switch channels.
    221     tailpid=$!
    222 
    223     # Restore stty to what they were before. We do want this
    224     # to expand now, so the shellcheck error can be ignored.
    225     trap 'stty $prestty; printf "\033[m" ; kill -TERM 0' EXIT INT QUIT
    226     stty -echonl ${NOTEXT:+-}echo
    227 
    228     printf '\033[m'
    229     while read -r msg; do
    230         # Go up, remove the line, and go back down.
    231         # Keeps it clean. If we have 'stty -echo' set,
    232         # we don't need to do this.
    233         [ "$NOTEXT" ] || printf '\033[A\033[K' >/dev/tty
    234 
    235         # Refresh the statusbar line.
    236         statusbar
    237 
    238         # shellcheck disable=2015
    239         case "$msg" in
    240             '') continue ;;
    241             :b) toggle NOBAR ; CHAN="${c:-.}" ; break ;;
    242             :c|:chan) getchan ; continue ;;
    243             ':c '*|':chan '*)
    244                 # Check if the channel exists beforehand
    245                 # so that the program outputs an error
    246                 # instead of exiting. Set a CHAN variable
    247                 # and break if the channel exists
    248                 if checkchan "${msg#:c* }" ; then
    249                     CHAN="${msg#:c* }"
    250                     break
    251                 else continue ; fi
    252             ;;
    253             :h|:help) gethelp ; continue ;;
    254             :hist*)
    255                 # Word splitting is intentional here.
    256                 # shellcheck disable=2086
    257                 [ ${msg#:hist} ] || { msg "Specify a number" ; continue ;}
    258                 h="${msg#:hist* }" CHAN="${c:-.}" ; break ;;
    259             :q) break ;;
    260             :t) toggle NOTEXT ; [ "$NOTEXT" ] && stty -echo || stty echo ; continue ;;
    261             ":n "*) printf '%s <NOTE> %s\n' "$(date +%s)" "${msg#:n }" >> "$outfile" ; continue ;;
    262             ":l "*) l="${msg#:l* }" CHAN="${c:-.}"; break ;;
    263             :m) mark; continue ;;
    264             :x) mark; break    ;;
    265             :s) toggle NOSERVER ; CHAN="${c:-.}" ; break ;;
    266             :v|:version) msg "${0##*/}-0.1.0" ; continue ;;
    267             :*) msg "${0##*/}: Unknown Command '$msg'" "Type :help to get commands" ; continue ;;
    268             '\:'*)
    269                 # User can send a smiley, or anything starting with
    270                 # a ':' as a message by adding a backslash.
    271                 msg=${msg#\\} ;;
    272             /msg) msg="/j ${msg#/msg}" ;;
    273             /names) msg="/names $c" ;;
    274             /q) printf '%s\n' "/q" >> "$infile" ; break ;;
    275         esac
    276         printf '%s\n' "$msg"
    277     done >> "$infile"
    278 
    279     [ "$CHAN" ] && changechan "$CHAN"
    280     
    281 }
    282 
    283 # Load a configuration file if it exists, very beneficial if
    284 # you don't want to set them on your shellrc.
    285 # shellcheck disable=1090
    286 [ -r "${XDG_CONFIG_HOME:-$HOME/.config}/yaicrc" ] &&
    287     . "${XDG_CONFIG_HOME:-$HOME/.config}/yaicrc"
    288 
    289 main "$@"