#!/bin/bash ## -*- encoding: utf-8 -*- vim:tabstop=8:shiftwidth=2 ## ## _人人人人人人_ ## > 突然の死 < ジェネレーター (Echo "sudden death" message) ##  ̄Y^Y^Y^Y^Y^Y ̄ ## Copyright (C) 2013-2022 SATOH Fumiyasu @ OSSTech Corp., Japan ## ## ## ## ## License: GNU General Public License version 3 ## ## Requirements: ## * bash(1) 4.0, ksh(1) 93u or zsh(1) 4.3 ## * openssl(1) 1.0.2 if you use Twitter ## ## How to install: ## ## $ mkdir -p $HOME/bin ## $ cd $HOME/bin ## $ wget -nv https://raw.githubusercontent.com/fumiyas/home-commands/master/echo-sd ## $ chmod +x echo-sd ## $ export PATH="$HOME/bin:$PATH" ## $ alias echo=echo-sd ## $ alias banner=echo-sd ## ## Examples for Command-line mode: ## ## $ echo-sd --help ## ... ## $ echo-sd ## _人人人人人人_ ## > 突然の死 < ##  ̄Y^Y^Y^Y^Y^Y ̄ ## $ echo-sd -t ## (Tweet message in Twitter) ## $ echo-sd ぬるぽっ!! ## _人人人人人人人_ ## > ぬるぽっ!! < ##  ̄Y^Y^Y^Y^Y^Y^Y ̄ ## $ echo-sd -v ガッ! ## _人人_ ## > ガ < ## > ッ < ## > ! < ##  ̄Y^Y ̄ ## $ echo-sd -p1 ズキュウウゥン!! ## _人人人人人人人人人人_ ## >           < ## >  ズキュウウゥン!!  < ## >           < ##  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄ ## $ echo-sd -vp1 ゴゴゴゴゴゴゴゴ 「世界」ッ! 時よ止まれ! ## _人人人人人_ ## >      < ## > 時  ┐ ゴ < ## > よ 世 ゴ < ## > 止 界 ゴ < ## > ま └  ゴ < ## > れ ッ ゴ < ## > ! ! ゴ < ## >     ゴ < ## >     ゴ < ## >      < ##  ̄Y^Y^Y^Y^Y ̄ ## ## Examples for CGI mode: ## ## $ GATEWAY_INTERFACE=CGI/1.0 QUERY_STRING=s=おっす!\&o=v echo-sd ## Content-Type: text/html; charset=UTF-8 ## ... ##
##   _人人_
##   > お <
##   > っ <
##   > す <
##   > ! <
##    ̄Y^Y ̄
##   

## ... ## ## On-line demo site: ## ## https://www.OSSTech.co.jp/cgi-bin/echo-sd ## ## TODO: ## ## * --rest (REST IN PEACE) ## * --center with --vertical ## ## Inspired by: ## ## * 突然の死ジェネレータ - powered by starwing.net, created by @karno. ## http://starwing.net/suddenly_death.html ## * 突然の死ジェネレータ - 純粋関数空間 ## http://tanakh.jp/tools/sudden.html ## * 元ネタ ## https://dic.nicovideo.jp/a/%E7%AA%81%E7%84%B6%E3%81%AE%E6%AD%BB ## if [[ ${0##*/} == echo-sd ]] && [[ ${zsh_eval_context-toplevel} == toplevel ]]; then set -u case "${BASH_VERSION-}" in [1-3].*) if type zsh >/dev/null 2>&1; then unset BASH_VERSION exec zsh "$0" ${1+"$@"} exit 1 fi echo "${0##*/}: ERROR: bash 4 or later required" 1>&2 exit 1 ;; esac fi typeset SD_arg0="$0" typeset SD_copyright='(C) 2013-2022 SATOH Fumiyasu @ OSSTech Corp., Japan' typeset SD_url='https://github.com/fumiyas/home-commands/blob/master/echo-sd' typeset SD_lang_orig SD_lang typeset SD_bc_tl SD_bc_t SD_bc_tr typeset SD_bc_l SD_bc_r typeset SD_bc_bl SD_bc_b SD_bc_br typeset -A SD_vc_map typeset SD_c_lf=' ' ## ====================================================================== function SD_die { SD_echo_command "${SD_arg0##*/}: ERROR: $1" 1>&2 SD_echo_command 1>&2 exit "${2-1}" } function SD_init { if [[ -n ${ZSH_VERSION-} ]]; then setopt SH_WORD_SPLIT setopt BSD_ECHO setopt KSH_GLOB setopt KSH_ARRAYS elif [[ -n ${BASH_VERSION-} ]]; then shopt -u xpg_echo else ## ksh if [[ $(echo -n) == -n ]]; then alias echo='print -r' fi fi typeset locale lang_ja lang SD_lang_orig="${LANG-}" if [[ ${SD_lang_orig#*.} != @(UTF-8|utf-8|UTF8|utf8) ]]; then if type locale >/dev/null 2>&1; then while read -r locale; do if [[ ${locale#*.} == @(UTF-8|utf-8|UTF8|utf8) ]]; then if [[ ${locale%.*} == ja_JP ]]; then lang_ja="$locale" else lang="$locale" fi fi done < <(locale -a) fi fi SD_lang="${lang_ja-${lang-ja_JP.UTF-8}}" SD_bc_tl="_" SD_bc_t="人" SD_bc_tr="_" SD_bc_l=">" SD_bc_r="<" SD_bc_bl=" ̄" SD_bc_b="Y^" SD_bc_br=" ̄" if [[ -n ${ZSH_VERSION-} ]]; then SD_vc_map=() else SD_vc_map= fi SD_vc_map[↑]=' → ' SD_vc_map[↓]=' ← ' SD_vc_map[←]=' ↑ ' SD_vc_map[→]=' ↓ ' SD_vc_map[。]=' ︒ ' SD_vc_map[、]=' ︑ ' SD_vc_map[ー]=' | ' SD_vc_map[─]=' | ' SD_vc_map[−]=' | ' SD_vc_map[-]=' | ' SD_vc_map[〜]=' ∫ ' SD_vc_map[~]=' ∫ ' SD_vc_map[/]=' \ ' SD_vc_map[…]=' ︙ ' SD_vc_map[‥]=' ︰ ' SD_vc_map[︙]=' … ' SD_vc_map[:]=' ‥ ' SD_vc_map[:]=' ‥ ' SD_vc_map[=]=' ॥ ' SD_vc_map[=]=' ॥ ' SD_vc_map[(]=' ⏜ ' SD_vc_map[\(]=' ⏜ ' SD_vc_map[)]=' ⏝ ' SD_vc_map[\)]=' ⏝ ' SD_vc_map[[]=' ⎴ ' SD_vc_map[\[]=' ⎴ ' SD_vc_map[]]=' ⎵ ' SD_vc_map[\]]=' ⎵ ' SD_vc_map[{]=' ⏞ ' SD_vc_map[\{]=' ⏞ ' SD_vc_map[<]=' ∧ ' SD_vc_map[\<]=' ∧ ' SD_vc_map[>]=' ∨ ' SD_vc_map[\>]=' ∨ ' SD_vc_map[}]=' ⏟ ' SD_vc_map[\}]=' ⏟ ' SD_vc_map[「]=' ┐' SD_vc_map[」]='└ ' SD_vc_map[【]=' ︗ ' SD_vc_map[】]=' ︘ ' SD_vc_map[〖]=' ︗ ' SD_vc_map[〗]=' ︘ ' SD_vc_map[「]=' ┐' SD_vc_map[」]='└ ' SD_vc_map[-]=' | ' SD_vc_map[ー]=' | ' SD_vc_map[,]=" ' " SD_vc_map[、]=' ` ' } function _SD_fill_string_by_char { typeset str char str="$1"; shift char="$1"; shift ## Why bash on ja_JP.UTF-8 locale matches '[¥]' with '︙' (and others?) #str="${str//[ -~。-゚¢£¥¦¬¯]/$char}" str="${str//[ -~。-゚¢£¦¬¯]/$char}" str="${str//¥/$char}" str="${str//[! ]/$char$char}" echo "$str" } function _SD_pad_space { printf "%${1}s" '' } function _SD_string_width { typeset str str=$(_SD_fill_string_by_char "$1" ' ') echo "${#str}" } function _SD_echo_with_padding { typeset str width str="$1"; shift width="$1"; shift echo "${str}$(_SD_pad_space "$((width-$(_SD_string_width "${1-$str}")))")" } function SD_echo_header { typeset opt typeset round="set" typeset bc_t="$SD_bc_t" bc_tl="$SD_bc_tl" bc_tr="$SD_bc_tr" bc_tc="" typeset bc_l="$SD_bc_l" bc_r="$SD_bc_r" while [[ $# -gt 0 ]]; do opt="$1"; shift case "$opt" in --no-round) round="" ;; --bc-[tlr]|--bc-t[clr]) if [[ ! -n ${1+set} ]]; then SD_die "Option value required: $opt" fi eval "bc_${opt##*-}=\"\$1\"" shift ;; --) break ;; -*) SD_die "Invalid option: $opt" ;; *) set -- "$opt" ${1+"$@"} break ;; esac done typeset width="$1"; shift typeset padding="$1"; shift typeset template left= typeset -a paddings=() if [[ -n ${round-} && $width -ge 2 && $padding -gt 0 ]]; then left=" " let width-- else left="" fi template=$(_SD_pad_space "$width") while [[ $padding -gt 0 ]]; do if [[ -n ${round-} ]] && [[ $width -ge 2 ]]; then template="${template#?}" let width-- fi paddings=( "$left$bc_l ${template// / } $bc_r" ${paddings[@]+"${paddings[@]}"} ) if [[ -n ${round-} ]] && [[ $width -ge 2 ]]; then template="${template#?}" left="$left " let width-- fi let padding-- done typeset top if [[ -n $bc_tc ]]; then width=$(_SD_string_width "$template") template="$(_SD_pad_space $((width / 2)))" top="${template// /$bc_t}$bc_tc${template// /$bc_t}" else template+=" " top="${template// /$bc_t}" fi echo "${left// / }$bc_tl${top// /}$bc_tr" for padding_s in ${paddings[@]+"${paddings[@]}"}; do echo "${padding_s// / }" done } function SD_echo_footer { typeset opt typeset round="set" typeset bc_b="$SD_bc_b" bc_bl="$SD_bc_bl" bc_br="$SD_bc_br" typeset bc_l="$SD_bc_l" bc_r="$SD_bc_r" while [[ $# -gt 0 ]]; do opt="$1"; shift case "$opt" in --no-round) round="" ;; --bc-[blr]|--bc-b[lr]) if [[ ! -n ${1+set} ]]; then SD_die "Option value required: $opt" fi eval "bc_${opt##*-}=\"\$1\"" shift ;; --) break ;; -*) SD_die "Invalid option: $opt" ;; *) set -- "$opt" ${1+"$@"} break ;; esac done typeset width="$1"; shift typeset padding="$1"; shift typeset template left= if [[ -n ${round-} && $width -ge 2 && $padding -gt 0 ]]; then left=" " let width-- fi template=$(_SD_pad_space "$width") while [[ $padding -gt 0 ]]; do if [[ -n ${round-} ]] && [[ $width -ge 2 ]]; then template="${template#?}" let width-- fi echo "${left// / }$bc_l ${template// / } $bc_r" if [[ -n ${round-} ]] && [[ $width -ge 2 ]]; then template="${template#?}" left="$left " fi let padding-- done template+=" " ## FIXME: Support --bc-bc typeset bottom bottom="${template// /$bc_b}" echo "${left// / }$bc_bl${bottom// /}$bc_br" } function SD_echo_horizontal { typeset opt typeset round typeset bc_tl bc_t bc_tr bc_l bc_r bc_bl bc_b bc_br typeset padding_top=0 padding_bottom=0 padding_left=0 padding_right=0 typeset center while [[ $# -gt 0 ]]; do opt="$1"; shift case "$opt" in --no-round) no_round="set" ;; --bc-[tblr]|--bc-t[lr]|--bc-b[lr]) if [[ ! -n ${1+set} ]]; then SD_die "Option value required: $opt" fi eval "bc_${opt##*-}=\"\$1\"" shift ;; --padding) if [[ ${1-BAD} == @(*[!0-9]*) ]]; then SD_die "Invalid option value: $opt ${1-UNSPECIFIED}" fi padding_top="$((($1 + 1) / 2))" padding_bottom="$padding_top" padding_left="$1" padding_right="$1" shift ;; --padding-top|--padding-bottom|--padding-left|--padding-right) if [[ ${1-BAD} == @(*[!0-9]*) ]]; then SD_die "Invalid option value: $opt ${1-UNSPECIFIED}" fi case "$opt" in --padding-top) padding_top="$1" ;; --padding-bottom) padding_bottom="$1" ;; --padding-left) padding_left="$1" ;; --padding-right) padding_right="$1" ;; esac shift ;; --center) center="set" ;; --) break ;; -*) SD_die "Invalid option: $opt" ;; *) set -- "$opt" ${1+"$@"} break ;; esac done export LANG="$SD_lang" typeset -a scripts=() typeset script width width_tmp typeset padding_left_s padding_right_s width=0 padding_left_s=$(_SD_pad_space "$padding_left") padding_right_s=$(_SD_pad_space "$padding_right") for script in ${1+"$@"}; do script="$padding_left_s$script$padding_right_s" scripts+=("$script") width_tmp=$(_SD_string_width "$script") if [[ $width_tmp -gt $width ]]; then width="$width_tmp" fi done if [[ -n ${center-} && $((width % 2)) -eq 1 ]]; then let width++ fi SD_echo_header \ ${no_round:+--no-round} \ --bc-tl "${bc_tl:-$SD_bc_tl}" \ --bc-t "${bc_t:-$SD_bc_t}" \ --bc-tr "${bc_tr:-$SD_bc_tr}" \ --bc-l "${bc_l:-$SD_bc_l}" \ --bc-r "${bc_r:-$SD_bc_r}" \ "$width" \ "$padding_top" \ ; typeset line for script in ${scripts[@]+"${scripts[@]}"}; do if [[ -n ${center-} ]]; then typeset padding=$(((width - $(_SD_string_width "$script")) / 2)) if [[ $padding -gt 0 ]]; then script="$(_SD_pad_space "$padding")$script" fi fi line="${bc_l:-$SD_bc_l} $(_SD_echo_with_padding "${script}" "$width") ${bc_r:-$SD_bc_r}" echo "${line// / }" done SD_echo_footer \ ${no_round:+--no-round} \ --bc-bl "${bc_bl:-$SD_bc_bl}" \ --bc-b "${bc_b:-$SD_bc_b}" \ --bc-br "${bc_br:-$SD_bc_br}" \ --bc-l "${bc_l:-$SD_bc_l}" \ --bc-r "${bc_r:-$SD_bc_r}" \ "$width" \ "$padding_bottom" \ ; export LANG="$SD_lang_orig" } function SD_echo_vertical { typeset opt typeset no_round typeset bc_tc bc_tl bc_t bc_tr bc_l bc_r bc_bl bc_b bc_br typeset padding_top=0 padding_bottom=0 padding_left=0 padding_right=0 typeset center typeset tanzaku while [[ $# -gt 0 ]]; do opt="$1"; shift case "$opt" in --no-round) no_round="set" ;; --bc-[tblr]|--bc-t[clr]|--bc-b[lr]) if [[ ! -n ${1+set} ]]; then SD_die "Option value required: $opt" fi eval "bc_${opt##*-}=\"\$1\"" shift ;; --padding) if [[ ${1-BAD} == @(*[!0-9]*) ]]; then SD_die "Invalid option value: $opt ${1-UNSPECIFIED}" fi padding_top="$((($1 + 1) / 2))" padding_bottom="$padding_top" padding_left="$1" padding_right="$1" shift ;; --padding-top|--padding-bottom|--padding-left|--padding-right) if [[ ${1-BAD} == @(*[!0-9]*) ]]; then SD_die "Invalid option value: $opt ${1-UNSPECIFIED}" fi case "$opt" in --padding-top) padding_top="$1" ;; --padding-bottom) padding_bottom="$1" ;; --padding-left) padding_left="$1" ;; --padding-right) padding_right="$1" ;; esac shift ;; --center) center="set" ;; --tanzaku) tanzaku="set" ;; --) break ;; -*) SD_die "Invalid option: $opt" ;; *) set -- "$opt" ${1+"$@"} break ;; esac done export LANG="$SD_lang" ## FIXME: Implement --center option typeset -a lines=() typeset line line_n typeset script script_n trailer trailer2 letter letter_width next letter_width=4 script_n=0 for script in ${1+"$@"}; do line_n=0 trailer="$script" while [[ -n $script ]]; do trailer="${trailer#?}" letter="${script%$trailer}" script="${script#?}" if [[ -n ${SD_vc_map[$letter]-} ]]; then line="${SD_vc_map[$letter]}" else trailer2="${trailer#?}" next="${script%$trailer2}" if [[ $next == @(゙|゚) ]] || [[ $letter$next == @([\?!][\?!]) ]]; then line=" $letter$next " trailer="$trailer2" script="${script#?}" else line=" ${letter} " fi fi if [[ -z ${lines[$line_n]-} ]]; then lines[$line_n]="$(_SD_pad_space $((script_n * letter_width)))" fi lines[$line_n]="$(_SD_echo_with_padding "${line}" $letter_width)${lines[$line_n]}" let line_n+=1 done while [[ $line_n -lt ${#lines[@]} ]]; do lines[$line_n]="$(_SD_pad_space $letter_width)${lines[$line_n]}" let line_n+=1 done let script_n+=1 done typeset line_width=$(((script_n - 1) * letter_width)) let line_width+=$padding_left+$padding_right typeset padding_left_s padding_right_s padding_left_s=$(_SD_pad_space "$padding_left") padding_right_s=$(_SD_pad_space "$padding_right") SD_echo_header \ ${no_round:+--no-round} \ ${bc_tc:+--bc-tc "$bc_tc"} \ --bc-tl "${bc_tl:-$SD_bc_tl}" \ --bc-t "${bc_t:-$SD_bc_t}" \ --bc-tr "${bc_tr:-$SD_bc_tr}" \ --bc-l "${bc_l:-$SD_bc_l}" \ --bc-r "${bc_r:-$SD_bc_r}" \ "$line_width" \ "$padding_top" \ ; for line in ${lines[@]+"${lines[@]}"}; do line="$padding_left_s$line$padding_right_s" echo "${bc_l:-$SD_bc_l}${line// / }${bc_r:-$SD_bc_r}" done SD_echo_footer \ ${no_round:+--no-round} \ --bc-bl "${bc_bl:-$SD_bc_bl}" \ --bc-b "${bc_b:-$SD_bc_b}" \ --bc-br "${bc_br:-$SD_bc_br}" \ --bc-l "${bc_l:-$SD_bc_l}" \ --bc-r "${bc_r:-$SD_bc_r}" \ "$line_width" \ "$padding_bottom" \ ; export LANG="$SD_lang_orig" } function SD_echo_command_help { { SD_bc_r="< ジェネレーター" SD_echo_command cat </\>/g;' \ ; } function _SD_urlencode { ## FIXME: Unsafe? if [[ -n ${1+set} ]]; then echo "$1" else cat fi \ |sed \ -e 's/%/%25/g;' \ -e 's/ /%20/g;' \ -e 's/#/%23/g;' \ -e 's/&/%26/g;' \ -e 's/+/%2B/g;' \ -e 's/;/%3B/g;' \ -e 's/=/%3D/g;' \ -e 's/?/%3F/g;' \ -e 's/\\/%5C/g;' \ -e 's/\^/%5E/g;' \ -e 's/{/%7B/g;' \ -e 's/|/%7C/g;' \ -e 's/}/%7D/g;' \ -e 's/~/%7E/g;' \ ; } function _SD_urldecode { ## FIXME: Support ksh echo -e "$(echo "$1" |sed 's/+/ /g;s/%\(..\)/\\x\1/g;')" } function SD_echo_cgi { typeset query="${QUERY_STRING-}" typeset param value typeset tweet text vertical padding square typeset -a prefaces=() typeset -a scripts=() while [[ -n $query ]]; do param="${query%%&*}" ## '\' is workaround for zsh 4.3.x bug that causes "* not found" error name="$(_SD_urldecode "${param%%\=*}")" value="$(_SD_urldecode "${param#*=}")" case "$name" in o|options) case "$value" in text) text="checked" ;; v|vertical) vertical="checked" ;; p|padding) padding="checked" ;; square) square="checked" ;; tanzaku) tanzaku="checked" padding="" ;; stress) stress="checked" ;; esac ;; p|prefaces) while IFS= read -r line; do prefaces+=("${line% }") done < <(echo "$value") ;; s|scripts) while IFS= read -r line; do scripts+=("${line% }") done < <(echo "$value") ;; tweet) tweet="set" ;; esac if [[ -n ${query##*&*} ]]; then break fi query="${query#*&}" done set -- \ ${vertical:+--vertical} \ ${padding:+--padding 1} \ ${square:+--square} \ ${tanzaku:+--tanzaku} \ ${stress:+--stress} \ ; if [[ ${#scripts[@]} -eq 1 ]] && [[ -z ${scripts[0]} ]]; then set -- ${1+"$@"} -- '突然の死' else set -- ${1+"$@"} -- ${scripts[@]+"${scripts[@]}"} fi if [[ -n ${tweet-} ]]; then echo -n 'Location: https://twitter.com/intent/tweet?text=' SD_echo_command "$@" \ |while IFS= read -r line; do echo -n "$line" |_SD_urlencode echo -n '%0A' done echo -n "${SD_CGI_FOOTER_TWEET-https://t.co/lNiygmWCSn}" |_SD_urlencode echo echo elif [[ -n ${text-} ]]; then echo 'Content-Type: text/plain; charset=UTF-8' echo for preface in ${prefaces[@]+"${prefaces[@]}"}; do echo "$preface" done SD_echo_command "$@" else echo 'Content-Type: text/html; charset=UTF-8' echo echo \ '' \ '> 突然の死 < ジェネレーター' \ '' \ '

> 突然の死 < ジェネレーター

' \ '
' \ ; echo -n '
' echo '" echo '" echo '" echo '
' echo '" echo '" echo '" echo '" echo '
' echo '' echo '' echo '
' echo '
' echo '' echo '
'
    for preface in ${prefaces[@]+"${prefaces[@]}"}; do
      _SD_escape_html "$preface"
    done
    SD_echo_command "$@" |_SD_escape_html
    echo '

' _SD_escape_html "$SD_copyright" echo '
' echo "$(_SD_escape_html "$SD_url")" echo '
' echo "${SD_CGI_FOOTER_HTML-}" echo '' fi } ## ====================================================================== : ${HOME="/nonexistent"} TWEET_CONF="${ECHO_SD_CONF-$HOME/.echo-sd.conf}" TWEET_OAUTH_CONSUMER_KEY="${ECHO_SD_OAUTH_CONSUMER_KEY-qMAuBmchzlryuQahRlnAeg}" TWEET_OAUTH_CONSUMER_SECRET="${ECHO_SD_OAUTH_CONSUMER_SECRET-JNSzHebO5N5h5nOcYFc1IILP8tqGP4fy28vCBBDFCA}" ## Import tweet.sh from https://github.com/fumiyas/Tweet.sh typeset Tweet_arg0="$0" typeset Tweet_lang='' typeset Tweet_conf_file="${TWEET_CONF-$HOME/.tweet.conf}" typeset Tweet_api_host="api.twitter.com" typeset Tweet_api_url="https://$Tweet_api_host/1.1" typeset Tweet_api_url_request_token="https://$Tweet_api_host/oauth/request_token" typeset Tweet_api_url_authorize_token="https://$Tweet_api_host/oauth/authorize" typeset Tweet_api_url_access_token="https://$Tweet_api_host/oauth/access_token" typeset Tweet_oauth_consumer_key="${TWEET_OAUTH_CONSUMER_KEY-C7IpNPso1IYdCweXYaJ0Q}" typeset Tweet_oauth_consumer_secret="${TWEET_OAUTH_CONSUMER_SECRET-LAsLscqNC4kBaDW8EtmxMIVCkY8nsw07NaN5PNBYuY}" typeset Tweet_oauth_access_token='' typeset Tweet_oauth_access_token_secret='' typeset Tweet_script_limit='140' typeset Tweet_c_lf=' ' function Tweet_error { echo "${Tweet_arg0##*/}: ERROR: $1" 1>&2 } function HTTP_browser { typeset url="$1"; shift typeset open= case $(uname) in Darwin) open="open" ;; CYGWIN_*) open="cygstart" ;; *) if [[ -f /etc/debian_version ]]; then open="sensible-browser" elif type xdg-open >/dev/null 2>&1; then open="xdg-open" elif [[ ${XDG_CURRENT_DESKTOP-} == GNOME || -n ${GNOME_DESKTOP_SESSION_ID-} ]] ; then open="gnome-open" elif [[ ${XDG_CURRENT_DESKTOP-} == KDE || ${KDE_FULL_SESSION-} == true ]] ; then open="kde-open" else typeset browsers="${BROWSER-}" if [[ -z $browsers ]]; then browsers="www-browser:links2:elinks:links:lynx:w3m" if [[ -n ${DISPLAY-} ]]; then browsers="x-www-browser:firefox:seamonkey:mozilla:epiphany:konqueror:chromium:chromium-browser:google-chrome:$browsers" fi fi typeset ifs_save="$IFS" typeset found= IFS=: for open in $browsers; do if type "$open" >/dev/null 2>&1; then found=set break fi done IFS="$ifs_save" if [[ -z $found ]]; then ## FIXME: Print error message return 1 fi fi ;; esac "$open" "$url" return $? } function HTTP_pencode { if [[ -n ${1+set} ]]; then typeset in="${1-}"; shift else typeset in IFS= read -r in fi typeset LC_ALL='C' typeset out= typeset char while [[ -n "$in" ]]; do char="${in:0:1}" case "$char" in [a-zA-Z0-9\-._~]) out+="$char" ;; *) out+=$(printf '%%%02X' "'$char") ;; esac in="${in:1}" done echo -n "$out" } function HTTP_pdecode { typeset in="${1//\\/\\\\}"; shift printf "${in//\%/\\x}" } function HTTPS_request { typeset url="$1"; shift typeset method="$1"; shift typeset url_tmp #typeset url_scheme="${url%%://*}" url_tmp="${url#*://}" typeset url_path="/${url_tmp#*/}" url_tmp="${url_tmp%%/*}" typeset url_host="${url_tmp%%:*}" if [[ $url_tmp == @(*:*) ]]; then typeset url_port="${url_tmp#*:}" else typeset url_port='443' fi typeset line typeset -l line_lower typeset cert_verify_error= http_ver= rcode= rmessage= content_type= body= { while IFS= read -r line; do line="${line% }" if [[ $line == HTTP/* ]]; then http_ver="${line%% *}" line="${line#* }" rcode="${line%% *}" rmessage="${line#* }" break fi if [[ $line == 'verify error':* ]]; then cert_verify_error="$line" fi done while IFS= read -r line; do line="${line% }" [[ -z $line ]] && break line_lower="$line" case "$line_lower" in content-type:*) content_type="${line#*: }" ;; esac done while IFS= read -r line; do line="${line% }" body+="$line$Tweet_c_lf" done body+="$line" } < <( { echo "$method $url_path HTTP/1.1" echo "Host: $url_host" echo "Connection: close" if [[ $method == 'POST' ]]; then echo 'Content-Type: application/x-www-form-urlencoded' fi while [[ $# -gt 0 ]]; do [[ $1 == '--' ]] && { shift; break; } echo "$1" shift done typeset query="${1-}" typeset LC_ALL='C' echo "Content-Length: ${#query}" echo echo -n "$query" } \ |openssl s_client \ -connect "$url_host:$url_port" \ -servername "$url_host" \ -verify_hostname "$url_host" \ -no_tls1 \ -no_ssl3 \ -crlf \ -quiet \ 2>&1 \ ; ) typeset rc='0' if [[ -n $cert_verify_error ]]; then rcode='500' rmessage="Invalid server certificate: $cert_verify_error" rc='1' elif [[ $rcode != 200 ]]; then rc='1' fi echo "$rcode $rmessage" echo "$content_type" echo echo -n "$body" return "$rc" } function HTTP_response_extract { typeset response="$1"; shift typeset name="$1"; shift typeset value= value="${response#*\&$name=}" value="${value#$name=}" if [[ $value == "$response" ]]; then return 1 fi value="${value%%\&*}" echo -n "$value" return 0 } function OAuth_nonce { printf '%04x%04x%04x%04x%04x%04x%04x%04x' \ $RANDOM \ $RANDOM \ $RANDOM \ $RANDOM \ $RANDOM \ $RANDOM \ $RANDOM \ $RANDOM \ ; } function OAuth_timestamp { date +%s } function OAuth_generate { typeset realm="$1"; shift typeset consumer_key="$1"; shift typeset consumer_secret="$1"; shift typeset token="$1"; shift typeset token_secret="$1"; shift typeset callback="$1"; shift typeset url="$1"; shift typeset method="$1"; shift typeset hmac_key="$consumer_secret&$token_secret" typeset -a oauth=( "oauth_consumer_key=$consumer_key" "oauth_signature_method=HMAC-SHA1" "oauth_version=1.0" "oauth_nonce=$(OAuth_nonce)" "oauth_timestamp=$(OAuth_timestamp)" ${token:+"oauth_token=$token"} ${callback:+"oauth_callback=$callback"} ) typeset oauth_string oauth_string=$( echo -n "$method&" HTTP_pencode "$url" echo -n '&' for pv in "$@" "${oauth[@]}"; do echo "$(HTTP_pencode "${pv%%=*}") $(HTTP_pencode "${pv#*=}")" done \ |sort \ |sed 's/ /%3D/;s/$/%26/' \ |tr -d '\n' \ |sed 's/%26$//' \ ; ) typeset oauth_signature oauth_signature=$( echo -n "$oauth_string" \ |openssl sha1 -hmac "$hmac_key" -binary \ |openssl base64 \ |HTTP_pencode \ ; ) typeset query= while [[ $# -gt 0 ]]; do query+="$1" [[ $# -gt 1 ]] && query+='&' shift done echo "Authorization: OAuth${realm:+ realm=$realm,}" typeset pv for pv in "${oauth[@]}"; do echo " $pv," done echo " oauth_signature=$oauth_signature" } function Tweet_string_length { typeset LC_ALL="$Tweet_lang" echo "${#1}" } function Tweet_init { if [[ -n ${ZSH_VERSION-} ]]; then setopt BSD_ECHO setopt KSH_GLOB setopt TYPESET_SILENT elif [[ -n ${BASH_VERSION-} ]]; then shopt -u xpg_echo else ## ksh if [[ $(echo -n) == -n ]]; then alias echo='print -r' fi fi typeset lang_orig="${LANG-}" typeset locale lang_ja lang if [[ ${lang_orig#*.} != @(UTF-8|utf-8|UTF8|utf8) ]]; then if type locale >/dev/null 2>&1; then while read -r locale; do if [[ ${locale#*.} == @(UTF-8|utf-8|UTF8|utf8) ]]; then if [[ ${locale%.*} == ja_JP ]]; then lang_ja="$locale" else lang="$locale" fi fi done < <(locale -a) fi fi Tweet_lang="${lang_ja-${lang-ja_JP.UTF-8}}" } function Tweet_authorize { if [[ -n $Tweet_oauth_access_token && -n $Tweet_oauth_access_token_secret ]]; then return 0 fi echo "No OAuth access token and/or secret for Twitter access configured." echo echo "I'll open Twitter site by a WWW browser to get OAuth access token" echo "and secret. Please authorize this application and get a PIN code" echo "on Twitter site." echo echo -n "Press Enter key to open Twitter site..." read echo typeset oauth oauth=$( OAuth_generate \ "$Tweet_api_url" \ "$Tweet_oauth_consumer_key" \ "$Tweet_oauth_consumer_secret" \ '' \ '' \ '' \ "$Tweet_api_url_request_token" \ "POST" \ ; ) typeset response response=$( HTTPS_request \ "$Tweet_api_url_request_token" \ "POST" \ "$oauth" \ ; ) typeset rc="$?" if [[ $rc -ne 0 ]]; then Tweet_error "OAuth request token failed: ${response%%$Tweet_c_lf*}" return 1 fi typeset body="${response#*$Tweet_c_lf$Tweet_c_lf}" typeset oauth_token=$(HTTP_response_extract "$body" oauth_token) typeset oauth_token_secret=$(HTTP_response_extract "$body" oauth_token_secret) HTTP_browser "$Tweet_api_url_authorize_token?oauth_token=$oauth_token" echo -n 'Enter PIN code: ' typeset pin= read -r pin echo typeset oauth oauth=$( OAuth_generate \ "$Tweet_api_url" \ "$Tweet_oauth_consumer_key" \ "$Tweet_oauth_consumer_secret" \ "$oauth_token" \ "$oauth_token_secret" \ '' \ "$Tweet_api_url_access_token" \ "POST" \ "oauth_verifier=$pin" \ ; ) typeset response response=$( HTTPS_request \ "$Tweet_api_url_access_token" \ "POST" \ "$oauth" \ -- \ "oauth_verifier=$pin" \ ; ) typeset rc="$?" if [[ $rc -ne 0 ]]; then Tweet_error "OAuth access token failed: ${response%%$Tweet_c_lf*}" return 1 fi typeset body="${response#*$Tweet_c_lf$Tweet_c_lf}" Tweet_oauth_access_token=$(HTTP_response_extract "$body" oauth_token) Tweet_oauth_access_token_secret=$(HTTP_response_extract "$body" oauth_token_secret) if [[ ! -f "$Tweet_conf_file" ]]; then echo "Saving OAuth consumer key and secret into $Tweet_conf_file..." (umask 0077; touch "$Tweet_conf_file") || return 1 echo "oauth_consumer_key='$Tweet_oauth_consumer_key'" >>"$Tweet_conf_file" echo "oauth_consumer_secret='$Tweet_oauth_consumer_secret'" >>"$Tweet_conf_file" fi echo "Saving OAuth access token and secret into $Tweet_conf_file..." echo "oauth_access_token='$Tweet_oauth_access_token'" >>"$Tweet_conf_file" echo "oauth_access_token_secret='$Tweet_oauth_access_token_secret'" >>"$Tweet_conf_file" return 0 } function Tweet_tweet { typeset script="$1"; shift typeset script_len=$(Tweet_string_length "$script") if [[ $script_len -gt $Tweet_script_limit ]]; then Tweet_error "Script too long (>$Tweet_script_limit) to tweet: $script_len" return 1 fi typeset query query="status=$(HTTP_pencode "$script")" typeset oauth oauth=$( OAuth_generate \ "$Tweet_api_url" \ "$Tweet_oauth_consumer_key" \ "$Tweet_oauth_consumer_secret" \ "$Tweet_oauth_access_token" \ "$Tweet_oauth_access_token_secret" \ '' \ "$Tweet_api_url/statuses/update.json" \ "POST" \ "$query" \ ; ) typeset response response=$( HTTPS_request \ "$Tweet_api_url/statuses/update.json" \ "POST" \ "$oauth" \ -- \ "$query" \ ; ) typeset rc="$?" if [[ $rc -ne 0 ]]; then Tweet_error "Tweet failed: ${response%%$Tweet_c_lf*}" return 1 fi typeset body="${response#*$Tweet_c_lf$Tweet_c_lf}" } function Tweet_command_help { echo "Usage: $0 SCRIPT" exit 0 } function Tweet_command { if [[ $# -ne 1 ]]; then Tweet_command_help exit 0 fi if [[ -f "$Tweet_conf_file" ]]; then . "$Tweet_conf_file" || exit 1 fi if [[ -n ${oauth_consumer_key-} ]]; then Tweet_oauth_consumer_key="$oauth_consumer_key" fi if [[ -n ${oauth_consumer_secret-} ]]; then Tweet_oauth_consumer_secret="$oauth_consumer_secret" fi if [[ -n ${oauth_access_token-} ]]; then Tweet_oauth_access_token="$oauth_access_token" fi if [[ -n ${oauth_access_token_secret-} ]]; then Tweet_oauth_access_token_secret="$oauth_access_token_secret" fi Tweet_authorize && Tweet_tweet "$@" ## FIXME: Parse reply from Twitter.com return $? } ## ====================================================================== if [[ ${0##*/} == echo-sd ]] && [[ ${zsh_eval_context-toplevel} == toplevel ]]; then SD_init if [[ ${GATEWAY_INTERFACE-} == @(CGI*) ]]; then SD_echo_cgi "$@" else SD_echo_command "$@" fi exit $? fi return 0