/home/alex/dev/lib/os.sh (1)

From RaySoft
# ------------------------------------------------------------------------------
# os.sh
# =====
#
# Project   Library
# Scope     macOS
# Copyright (C) 2024 by RaySoft, Zurich, Switzerland
# License   GNU General Public License (GPL) 2.0
#           https://www.gnu.org/licenses/gpl2.txt
#
# ------------------------------------------------------------------------------
#
# WARNING: This library uses functions from the 'nf.sh' library!
#
# ------------------------------------------------------------------------------

# Exit if the library is run as a script and not sourced as a library
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
  echo "Don't run this script! Just source it."
  exit 1
fi

# Return if the library is already loaded
if declare -F 'os' >'/dev/null' 2>&1; then
  return 0
fi

# Return if a needed library is not loaded
for lib in 'nf'; do
  if ! declare -F "${lib}" >'/dev/null' 2>&1; then
    echo "The needed library '${lib}.sh' is not loaded!"
    return 1
  fi
done

# ------------------------------------------------------------------------------

os() {
  # Show all function of this library.
  #
  # Arguments:
  #   none
  #
  # Returns:
  #   0:  Success
  #   >0: Error

  nf::print_heading '-' 'ba' "My 'Operating System' Library"

  # Print all functions starting with the prefix 'xx::' but ignore private
  # functions with the prefix 'xx::_'
  compgen -c "${FUNCNAME[0]}::" | grep -v "${FUNCNAME[0]}::_"

  echo

  return 0
}

# ------------------------------------------------------------------------------

os::change_space() {
  # Change the space in macOS.
  #
  # NOTE: This only works if Switch to Desktop [1-6] is activated in
  #       System Preferences => Keyboard => Shortcuts
  #
  # Arguments:
  #   $1: Space number [1-6], start counting by 1
  #
  # Returns:
  #   0:  Success
  #   >0: Error

  # Return if the function was called incorrectly
  if [[ "$#" -ne 1 || ! "$1" =~ ^[1-6]$ ]]; then
    nf::carp "Error calling function: $*!"
    return 1
  fi

  local bin=''

  # Return if a needed binary is not available
  for bin in 'osascript'; do
    if ! type -P "${bin}" >'/dev/null' 2>&1; then
      nf::carp "Error finding binary: ${bin}!"
      return 1
    fi
  done

  # Define a local variable
  local key=18

  # Get the key for a certain space
  case "$1" in
    1) key=18 ;;
    2) key=19 ;;
    3) key=20 ;;
    4) key=21 ;;
    5) key=23 ;; # This is not an error!
    6) key=22 ;;
  esac

  # Run the AppleScript to perform the key stroke
  if ! osascript <<-EOS ; then
		tell application "System Events"
		  key code ${key} using {control down}
		end tell

		delay 0.1
	EOS
    nf::carp 'Error running AppleScript!'
    return 1
  fi

  return 0
}

# ------------------------------------------------------------------------------

os::mkdir_ifn() {
  # Make directories if they do not exist.
  #
  # Arguments:
  #   $@: List of directories
  #
  # Returns:
  #   0:  Success
  #   >0: Error

  # Define local variables
  local path=''

  # For all directories
  for path in "$@"; do
    # If the directory doesn't exist
    if [[ ! -d "${path}" ]]; then
      # Continue if a file with this name already exist
      if [[ -e "${path}" ]]; then
        nf::carp "File already exists: ${path}!"
        continue
      fi

      # Make the directory and if needed its parent directories
      if ! mkdir -p "${path}"; then
        nf::carp "Error making directory: ${path}!"
        return 1
      fi
    fi
  done

  return 0
}

# ------------------------------------------------------------------------------

os::run_in_iterm() {
  # Opens a new iTerm window and runs the given command.
  #
  # Arguments:
  #   $@: Command with parameters
  #
  # Returns:
  #   0:  Success
  #   >0: Error
  #
  # Example:
  #   os::run_in_iterm ssh 'alex@raysoft.loc'

  # Returns if the function was called incorrectly
  if [[ "$#" -eq 0 ]]; then
    nf::carp 'Error calling function!'
    return 1
  fi

  local bin=''

  # Returns if a needed binary is not available
  for bin in 'osascript'; do
    if ! type -P "${bin}" >'/dev/null' 2>&1; then
      nf::carp "Error finding binary: ${bin}!"
      return 1
    fi
  done

  # Runs the AppleScript to open a new iTerm windows and executes the 'tail'
  # command in it
  if ! osascript <<-EOS >'/dev/null' 2>&1; then
		tell application "iTerm"
		  set newWindow to (create window with default profile)
		  tell newWindow
		    tell current session of newWindow to write text "$*"
		  end tell
		end tell
	EOS
    nf::carp 'Error running AppleScript!'
    return 1
  fi

  return 0
}

# ------------------------------------------------------------------------------

os::set_permissions() {
  # Set permissions for a list of files or directories.
  #
  # Arguments:
  #   $1: Owner's user
  #   $2: Owner's group
  #   $3: Permissions for files
  #   $4: Permissions for directories
  #   $@: List of files or directories
  #
  # Returns:
  #   0:  Success
  #   >0: Error
  #
  # Example:
  #   os::set_permissions 'alex' 'users' 0600 0700 "${HOME}"

  # Return if the function was called incorrectly
  if [[ "$#" -lt 5 || ! "$1" =~ ^[a-z][-a-z0-9]*$ || ! "$2" =~ ^[a-z][-a-z0-9]*$ \
     || ! "$3" =~ ^[0-7]{3,4}$ || ! "$4" =~ ^[0-7]{3,4}$ ]]
  then
    nf::carp "Error calling function: $*!"
    return 1
  fi

  # Define local variables
  local path=''

  # For all files or directories
  for path in "${@:5}"; do
    # Return if the file or directory doesn't exist
    if [[ ! -e "${path}" ]]; then
      nf::carp "Error finding directory: ${path}!"
      continue
    fi

    # Search for all files and directories, check if the owner and file
    # permissions are correct and adjust them if necessary
    if ! find "${path}" \
            '!' '(' -user "$1" -group "$2" ')' \
            -execdir sudo chown "$1.$2" '{}' '+' \
            -printf "Changing owner for %p from %u.%g to $1.$2\\n" \
          ',' \
            -type 'f' '(' \
              '!' -perm "$3" \
              -execdir sudo chmod "$3" '{}' '+' \
              -printf "Changing mode for %p from %#m to $3\\n" \
            ')' \
            -or -type 'd' '(' \
              '!' -perm "$4" \
              -execdir sudo chmod "$4" '{}' '+' \
              -printf "Changing mode for %p from %#m to $4\\n" \
            ')'
    then
      nf::carp "Error changing permissions: ${path}!"
      return 1
    fi
  done

  return 0
}

# ------------------------------------------------------------------------------

os::test_env() {
  # Test the environment set by the calling script by validating the variables
  # and making them read only.
  #
  # Arguments:
  #   $1: Variable prefix
  #
  # Returns:
  #   0:  Success
  #   >0: Error

  # Return if the function was called incorrectly
  if [[ "$#" -ne 1 && ! "$1" =~ ^[A-Z]+$ ]]; then
    nf::carp "Error calling function: $*!"
    return 1
  fi

  local name=''
  local var=''

  while read -r name; do
    if [[ "${name}" =~ ^$1_[DF]._ ]]; then
      local array="${name}[@]"

      for var in "${!array}"; do
        if [[ "${name}" =~ ^$1_DE_ && ! -d "${var}" ]]; then
          nf::carp "Error finding directory: ${var}!"
          return 1
        elif [[ "${name}" =~ ^$1_DN_ && ! -d "${var}" ]]; then
          if ! os::mkdir_ifn "${var}"; then
            return 1
          fi
        elif [[ "${name}" =~ ^$1_FE_ && ! -f "${var}" ]]; then
          nf::carp "Error finding file: ${var}!"
          return 1
        fi
      done
    elif [[ "${name}" =~ ^$1_X_ ]]; then
      if [[ ! ( -f "${!name[0]}" && -x "${!name[0]}" ) ]]; then
        nf::carp "Error finding executable: ${!name[0]}!"
        return 1
      fi
    fi

    if ! declare -r "${name}" >'/dev/null' 2>&1; then
      nf::carp "Error setting variable to read only: ${name}!"
      return 1
    fi
  done < <(compgen -v "$1_")

  return 0
}

# ------------------------------------------------------------------------------

return 0

Usage

NOTE:
This script uses functions from the library ~/dev/lib/nf.sh.
# Path to the global libraries
XX_DE_GLOBAL_LIB="${HOME}/dev/lib"

# Global libraries to be loaded
XX_GLOBAL_LIBS=('nf' 'os')
# Test if library path is available
if [[ ! -d "${XX_DE_GLOBAL_LIB}" ]]; then
  echo "Error finding directory: ${XX_DE_GLOBAL_LIB}!"
  exit 1
fi

# Load libraries
for lib in "${XX_GLOBAL_LIBS[@]}"; do
  lib="${XX_DE_GLOBAL_LIB}/${lib}.sh"

  if [[ ! -f "${lib}" ]]; then
    echo "Error finding library: ${lib}!"
    exit 1
  fi

  if ! source "${lib}"; then
    exit 1
  fi
done