#!/bin/sh
# ------------------------------------------------------------------------------
#
# git-qguard.sh: Implement the Git-MQ "git qguard" extension command.
#
# Set, reset (clear), or display guards on any one Git-MQ patch, or display
# guards on all patches in the series.
#
# $Id$
#
# ------------------------------------------------------------------------------
#
mq_facility="git qguard"

# I'd have liked to call this a "SYNOPSIS", (which is what it is), but git's
# git-sh-setup script requires the much less appropriate name "OPTIONS_SPEC",
# (which describes only a small subset of its actual content).
#
OPTIONS_SPEC="\
git qguard [-n | --none] [<patch>] [-- [+<guard>] ... [-<guard>] ...]
git qguard [-l | --list]

Set, reset, or show guards on the topmost applied patch, on a specified
patch, or (with the '--list' option) show guards on all patches.
--
l,list!   list all patches, and show all associated guards
n,none!   reset (clear) all guards on specified patch"
#
# ------------------------------------------------------------------------------
#
# $Id$
#
# Written by Keith Marshall <keith@users.osdn.me>
# Copyright (C) 2018-2020, 2022, Keith Marshall
#
#
# This file is part of the Git-MQ program suite.
#
# The Git-MQ program suite is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public Licence
# as published by the Free Software Foundation, either version 3 of
# the Licence, or (at your option) any later version.
#
# The Git-MQ program suite 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 Licence for more details.
#
# You should have received a copy of the GNU General Public Licence
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# ------------------------------------------------------------------------------
#
# Although they may not always have any effect, all Git-MQ commands, like
# their hg counterparts, are expected to accept the global verbosity, and
# colour control options; ensure that they are declared:
#
OPTIONS_SPEC="$OPTIONS_SPEC
colour?*     generic output colour control -- may have no effect"
${OPTION_VERBOSE_DEFINED-false} || OPTIONS_SPEC="$OPTIONS_SPEC
v,verbose!*  generic verbosity selector -- may have no effect"

# For Git-MQ options, such as "--colour", we prefer a spelling convention
# which conforms to "World English" standards; however, git itself adopts
# "US English" convention.  Thus, we must also accommodate users who will
# specify "--color" instead of "--colour", without creating any ambiguity
# in the possible abbreviations; to achieve this, we check for "--color",
# among the command-line arguments, replacing it with "--colour", BEFORE
# we allow git to parse them.
#
for mq_argv
do case "$mq_argv" in
     --color*) mq_argv=`echo $mq_argv | sed 's/^--colo/&u/'` ;;
     --no-color) mq_argv="--no-colour" ;;
   esac
   ${mq_argv_init-true} && { set -- "$mq_argv"; mq_argv_init=false
   } || set -- "$@" "$mq_argv"
done

# Now, we may let git parse the command line, and set up its shell script
# processing environment, for use within a git working directory tree.
#
SUBDIRECTORY_OK=true . "`git --exec-path`/git-sh-setup" && require_work_tree

libexecdir=`dirname "$0"`
test `basename "$libexecdir"` = bin && libexecdir=`dirname "$libexecdir"`
libexecdir="$libexecdir/libexec/git-mq/${VERSION=1.0}"

mq_require(){ test -f "$1" || die "fatal: '$1' not found."; . "$1"; }
mq_require "$libexecdir/git-mq-setup.sh"

# Parse any command line options, which the user may have specified;
# the git_mq_getopt function will implicitly handle the default global
# options, but we must explicitly handle command specific effects.
#
while git_mq_getopt "$@"
do case $1 in
     -l) mq_list_option=true ;;
     -n) mq_drop_option=true ;;
   esac; shift
done

# All actions to be performed by this command must operate on all of
# the "guards", "series", and "status" files; those which display any
# guard information may colour code the output.
#
mq_require mq-series-list-colour
mq_map_control_file_refs "$mq_patchdir" guards series status

# The following "awk" function provides the colour coding for any one
# output record at a time.
#
mq_print_guards="$mq_colour"'
  function print_guards( entry ) {
    $1 = mq_apply_colour( $1 )":";
    if( NF == 1 ) $2 = mq_apply_colour( "unguarded", "A" );
    else for( fld = 1; NF >= fld; fld++ )
    { if( index( $fld, "#" ) != 1 ) continue;
      switch( substr( $fld, 2, 1 ) )
      { case "+": $fld = mq_apply_colour( substr( $fld, 2 ), "G" ); break;
	case "-": $fld = mq_apply_colour( substr( $fld, 2 ), "D" ); break;
      }
    }
    print;
    return entry;
  }
'
# If specified, the '--list' option precludes specification of '--none',
# or any argument...
#
${mq_list_option-false} && {
  { test $# -gt 0 || ${mq_drop_option-false}; } &&
    $fatal "'--none' and/or arguments forbidden with '--list'"

# ...but when specified in isolation, report the guard state of all
# patches which are listed in the series file, then exit.
#
  git_mq_enable_colour_pager awk "$mq_guards $mq_series $mq_status
    $mq_print_guards"' END { for( idx = 0; entries > idx; idx++ )
      print_guards( $0 = series[idx] );
    }' "$mq_guards_file" "$mq_series_file" "$mq_status_file"
  exit 0
}

# When NOT simply listing all guards which are specified in the series
# file, then we need to be prepared to update the series file; we must
# establish a transaction cache, to capture the updates.
#
mq_require mq-tmpfile
mq_tmpfile mq_series_file_tmp '`mktemp --tmpdir="$mq_patchdir"` ||
  $fatal "cannot establish series file update cache"'

# Any action, other than that associated with the "--list" option, will
# operate primarily on a single patch, but must parse the entire patch
# series, (and may rewrite the entire series file); the following "awk"
# script handles any guards manipulation for a single patch, or display
# of the guard assignments for any one patch, whilst preparing a (maybe
# modified) copy of the series file, for possible update.
#
# FIXME: this will simply ignore the "--none" option, when accompanied
# by additional guard specifications; although it makes little sense to
# allow this combination, the current behaviour is consistent with that
# of Mercurial's implementation, (as it is in hg version 2.7.2).
#
printf '%s\n' "$@" | awk "$mq_guards $mq_series $mq_status $mq_print_guards"'
  function complain(msg){ print "'"$mq_facility"': "msg | "cat >&2"; }
  FILENAME == "-" && /^[+-]/ { guard_args = guard_args" #"$0; }
  FILENAME == "-" && /^[^+-]/ {
    if( patchname )
    { complain( "error: invalid guard specification \47"$0"\47" );
      complain( "error: initial character must be \47+\47 or \47-\47" );
      exit status = 2;
    }
    else
    { if( !((patchname = $0) in entry) )
      { complain( "error: there is no \47"patchname"\47 in the patch series" );
	exit status = 2;
      }
    }
  }
  END { if( status ) exit status;
    if( ! patchname )
    { if( applied >= 0 ){ $0 = series[applied]; patchname = $1; }
      else { complain( "error: no patches have been applied" ); exit 2; }
    }
    for( idx = 0; entries > idx; idx++ )
    { $0 = series[idx]; if( $1 == patchname )
      { if( guard_args || '"`${mq_drop_option-false} && echo 1`"'0 )
	  $0 = patchname guard_args;
	else $0 = print_guards( series[idx] );
      }
      print > "'"$mq_series_file_tmp"'";
    }
  }
' "$mq_guards_file" "$mq_series_file" "$mq_status_file" - &&
  mq_update mq_series_file
#
# ------------------------------------------------------------------------------
# $RCSfile$: end of file
