# Copyright (C) 2013, 2016 Patrick "P. J." McDermott # # This file is part of libsh. # # libsh is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # libsh is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with libsh. If not, see # . # Shells and the file descriptors they reserve for the user: # * Debian Almquist Shell # - The shell doesn't understand any file descriptor greater than 9. # - File descriptors starting at 10 are used for I/O redirection. # * BusyBox ash # - File descriptors starting at 10 are used for job control and I/O # redirection. # * GNU Bash: # - GNU Readline uses file descriptor 255 in interactive shells. # - File descriptors starting at 10 are used for I/O redirection. # * MirOS Korn Shell # - File descriptors starting at either 10 or 24 are used for the shell's # tty_fd and shl_dbg_fd. # * Solaris ksh # - The shell doesn't understand any file descriptor greater than 9. static fd_min=3 static fd_max=9 static fd_3_set=false static fd_4_set=false static fd_5_set=false static fd_6_set=false static fd_7_set=false static fd_8_set=false static fd_9_set=false fd=0 fopen(string path, string mode) { local mode_r= local mode_w= local i= local f= case "${mode}" in 'r') mode_r=true mode_w=false mode='<' ;; 'r+') mode_r=true mode_w=true mode='<>';; 'w') mode_r=false mode_w=true mode='>' ;; 'a') mode_r=false mode_w=true mode='>>';; *) return ${EINVAL};; esac # Find first available file descriptor. i=${fd_min} while [ ${i} -le ${fd_max} ]; do if ! $(eval "printf '%s' \${fd_${i}_set}"); then f=${i} break fi i=$(($i + 1)) done if [ "x${f}" = 'x' ]; then return ${EMFILE} fi # Check file mode and existence. if ${mode_r} && ! [ -r "${path}" ]; then return ${EACCES} fi if ${mode_w} && ! [ -w "${path}" ]; then return ${EACCES} fi if ! [ -e "${path}" ]; then return ${ENOENT} fi if [ -d "${path}" ]; then return ${EISDIR} fi if ! eval "exec ${f}${mode}'${path}'" 2>/dev/null; then return ${EUNK} fi eval "fd_${f}_set=true" fd="${f}" return 0 } fclose(int f) { local f=${1} # Make sure the file descriptor is valid and open. if [ ${f} -lt ${fd_min} ] || [ ${f} -gt ${fd_max} ]; then return ${EINVAL} fi if $(eval "printf '%s' \${fd_${i}_set}"); then return ${EBADF} fi eval "exec ${f}>&-" unset "fd_${f}" return 0 }