summary refs log tree commit diff
path: root/Functions/TCP/tcp_close
blob: 9c876786c0e67c57ee2bcf59a8a1497478fa48f5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# Usage:
#   tcp_close [-q] [ -a | session ... ]
# -a means all sessions.
# -n means don't close a fake session's fd.
# -q means quiet.
#
# Accepts the -s and -l arguments for consistency with other functions,
# but there is no particular gain in using them
emulate -L zsh
setopt extendedglob cbases

local all quiet opt alias noclose
local -a sessnames

while getopts "aql:ns:" opt; do
    case $opt in
	(a) all=1
	    ;;
	(q) quiet=1
	    ;;
	(l) sessnames+=(${(s.,.)OPTARG})
	    ;;
	(n) noclose=1
	    ;;
	(s) sessnames+=($OPTARG)
	    ;;
	(*) return 1
	    ;;
    esac
done

(( OPTIND > 1 )) && shift $(( OPTIND - 1))

if [[ -n $all ]]; then
    if (( $# )); then
	print "Usage: $0 [ -q ] [ -a | [ session ... ] ]" >&2
	return 1
    fi
    sessnames=(${(k)tcp_by_name})
    if (( ! ${#sessnames} )); then
	[[ -z $quiet ]] && print "No TCP sessions open." >&2
	return 1
    fi
fi

sessnames+=($*)

if (( ! ${#sessnames} )); then
    sessnames+=($TCP_SESS)
fi

if (( ! ${#sessnames} )); then
    [[ -z $quiet ]] && print "No current TCP session." >&2
    return 1
fi

local tcp_sess fd
integer stat curstat

# Check to see if the fd is opened for a TCP session, or was opened
# to a pre-existing fd.  We could remember this from tcp_open.
local -A ztcp_fds
local line match mbegin mend

if zmodload -e zsh/net/tcp; then
    ztcp | while read line; do
        if [[ $line = (#b)*fd\ ([0-9]##) ]]; then
	    ztcp_fds[$match[1]]=1
	fi
    done
fi

for tcp_sess in $sessnames; do
    curstat=0
    fd=${tcp_by_name[$tcp_sess]}
    if [[ -z $fd ]]; then
	print "No TCP session $tcp_sess!" >&2
	stat=1
	continue
    fi
    # We need the base name if this is an alias.
    tcp_sess=${tcp_by_fd[$fd]}
    if [[ -z $tcp_sess ]]; then
	if [[ -z $quiet ]]; then
	    print "Aaargh!  Session for fd $fd has disappeared!" >&2
	fi
	stat=1
	continue
    fi

    if [[ ${+tcp_aliases} -ne 0 && -n ${tcp_aliases[$fd]} ]]; then
	for alias in ${=tcp_aliases[$fd]}; do
	    if (( ${+functions[tcp_on_unalias]} )); then
		tcp_on_unalias $alias $fd
	    fi
	    unset "tcp_by_name[$alias]"
	done
	unset "tcp_aliases[$fd]"
    fi

    # Don't return just because the zle handler couldn't be uninstalled...
    if [[ -o zle ]]; then
	zle -F $fd || print "[Ignoring...]" >&2
    fi

    if [[ -n $ztcp_fds[$fd] ]]; then
        # It's a ztcp session.
	if ! ztcp -c $fd; then
	    stat=1
	    curstat=1
	fi
    elif [[ -z $noclose ]]; then
        # It's not, just close it normally.
        # Careful: syntax for closing fd's is quite strict.
	if [[ ${#fd} -gt 1 ]]; then
	    [[ -z $quiet ]] && print "Can't close fd $fd; will leave open." >&2
	else
	    eval "exec $fd>&-"
	fi
    fi

    unset "tcp_by_name[$tcp_sess]"
    unset "tcp_by_fd[$fd]"
    if [[ -z $quiet && $curstat -eq 0 ]]; then
	print "Session $tcp_sess successfully closed."
    fi
    [[ $tcp_sess = $TCP_SESS ]] && unset TCP_SESS

    if (( ${+functions[tcp_on_close]} )); then
	tcp_on_close $tcp_sess $fd
    fi
done

return $stat