#!/bin/bash # # Copyright (C) 2013, 2014, 2015, 2016 Linaro, Inc # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program 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 General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # NOTICE: This service is most reliable when passed a full URL (including # service identifier, e.g., http://, git://). # # The get_git_ functions all operate on the same premise: # # When given a string that represents a git url or git repository # identifier (plus branch and/or revision information) they will # parse the URL for the requested part. # # Forms: # # get_git_service # The valid git services are: 'git', 'http' and 'ssh'. # # get_git_user # Valid usernames are: 'username' and 'multi.part.username". # # get_git_repo # The name of the respository including the '.git' suffix. # # get_git_tool # The same as 'repo' but minus the '.git' suffix. # # get_git_url # The full url of the repository, minus branch and/or revision # information. This is valid for passing to git. # # get_git_branch # The branch designation that follows the repository, e.g., # binutils.git/branch # # get_git_tag # Return a sanitized string of repository concatenated with optional # branch and revision information. The branch name has all '/' # characters converted to '-' characters. # # WARNING: A git tag is not parseable by the git parser. It's a one # way translation to be used for naming entities only. # # For example, calling get_git_tag with the following: # git://foo.com/repo.git~multi/slash/branch # Will return the following: # repo.git~multi-slash-branch as the 'tag'. # # get_git_revision # The revision designation that follows the repository, e.g., # binutils.git@12345 # # Note: abe allows .git/branch@revision even though # when a revision is present, the 'branch' is only used # in path names, since a revision implies a branch already. # # Calling convention: # # Because the get_git_ functions return a string in stdout # they must be called in a subshell using the following convention: # # local out= # out="$(get_git_ )" # if test $? -gt 1; then # # Parser detected malformed input. Depending on what # # you expect, this might or might-not be an error. # elif test x"${out}" = x; then # # Parser didn't parse the requested part because # # it probably wasn't in the string OR the input # # is malformed. # fi # # Return Value # stdout: Returns the requested string (if parsed) # $?: Returns the error status code # stderr: If $? != 0 then this contains information about # malformed input. # # Input String: # Valid inputs: # [{git|http}://[@]{|127.0.0.1}/path/].git[/][@ functions. git_parser() { local part=$1 local in=$2 local service= local revision= local numfields= local numats= local user= local url= local tool= local repo= local branch= # Set to '1' if something in ${in} is malformed. local err=0 local service="$(echo "${in}" | sed -n ' s#\(^git\)://.*#\1#p; s#\(^ssh\)://.*#\1#p; s#\(^http\)://.*#\1#p; s#\(^https\)://.*#\1#p; ')" # Do this early because this is called often and we don't need all that # other parsing in this case. if test x"${part}" = x"service"; then # An http service with .git in the url is actually a git service. if test "$(echo ${service} | grep -c http)" -gt 0 -a "$(echo ${in} | egrep -c "\.git")" -gt 0; then service="git" # An ssh service is actually a git service. elif test x"${service}" = x"ssh"; then service="git" fi echo ${service} return 0 fi # Just bail out early if this is a launch pad service. if test x"${service}" = x"lp"; then case ${part} in repo) local repo="" repo="$(echo ${in} | sed -e "s#lp:[/]*##" -e 's:/.*::')" echo "${repo}" ;; branch) local hastilde="$(echo "${in}" | grep -c '\~')" local hasslash="$(echo "${in}" | grep -c '\/')" if test ${hastilde} -gt 0; then # Grab everything to the right of the ~ branch="$(echo ${in} | sed -e 's:[^~]*~\(.*\):\1:')" elif test ${hasslash} -gt 0; then branch="$(basename ${in})" fi echo ${branch} # otherwise there's no branch. ;; url) echo "${in}" ;; tool) # Strip service information and any trailing branch information. local tool="$(echo ${in} | sed -e 's/lp://' -e 's:/.*::')" # Strip superflous -linaro tags local tool="$(echo ${tool} | sed -e 's:-linaro::')" echo ${tool} ;; *) ;; esac return 0 fi # This is tarball and it is unique if test "$(echo ${in} | egrep -c "\.tar")" -gt 0; then case ${part} in repo) local repo="" repo="$(basename ${in})" repo="$(echo ${repo} | sed -e 's:-[0-9].*::')" echo "${repo}" ;; url) echo "${in}" ;; tool) # Special case binutils-gdb tool="$(echo ${in} | sed -e 's:\(binutils-gdb\).*:\1:g')" if test x"${tool}" != "xbinutils-gdb"; then # Otherwise only grab up to the first - tool="$(echo ${in} | sed -e 's:\([^-]*\)-.*:\1:g')" fi # Strip service or directory information. tool="$(basename ${tool})" echo ${tool} ;; tag) local tag= tag="$(echo ${in} | sed -e 's:\.tar.*::' -e 's:-[0-9][0-9][0-9][0-9]\.[0-9][0-9].*::')" echo ${tag} ;; *) ;; esac return 0 fi # This will only find a username if it follows the :// # and precedes the first / in the url. Yes you could # get away with http://www@.foo.com/. local user="$(echo "${in}" | sed -n "s;^${service}://\([^/]*\)@.*;\1;p")" # This will only find a revision if it is a sequence of # alphanumerical characters following the last @ in the line. local revision="$(echo "${in}" | sed -n 's/.*@\([[:alnum:]]*$\)/\1/p')" local hasdotgit="$(echo "${in}" | grep -c "\.git")" local hastilde="$(echo "${in}" | grep -c '\~')" # Strip out the ::// part. local noservice="$(echo "${in}" | sed -e "s#^${service}://##")" local secondbase= if test ${hasdotgit} -gt 0; then local secondbase="$(echo "${noservice}" | sed -e 's#.*\([/].*.git\)#\1#' -e 's#^/##' -e 's#@[[:alnum:]|@]*$##')" local repo="$(echo ${secondbase} | sed -e 's#\(.*\.git\).*#\1#' -e 's#.*/##')" if test ${hastilde} -gt 0; then local branch="$(echo "${secondbase}" | sed -n 's#[^~]*~\(.*\)$#\1#p')" if test "$(echo ${branch} | grep -c "^/")" -gt 0; then error "Malformed input. Superfluous / after ~. Stripping." err=1 local branch="$(echo "${branch}" | sed -e 's#^/##')" fi else local branch="$(echo ${secondbase} | sed -e 's#.*\.git##' -e 's#^[/]##' -e 's#@[[:alnum:]|@]*$##')" fi elif test ${hastilde} -gt 0 -a ${hasdotgit} -lt 1; then # If the service is part of the designator then we have to strip # up to the leading / if test x"${service}" != x; then local secondbase="$(echo "${noservice}" | sed -e "s#[^/]*/##")" else # Otherwise we process it as if the repo is the leftmost # element. local secondbase=${in} fi # We've already processed the revision so strip that (and any trailing # @ symbols) off. local secondbase="$(echo "${secondbase}" | sed -e 's#@[[:alnum:]|@]*$##')" local branch="$(echo "${secondbase}" | sed -n 's#[^~]*~\(.*\)$#\1#p')" if test "$(echo ${branch} | grep -c "^/")" -gt 0; then error "Malformed input. Superfluous / after ~. Stripping." err=1 local branch="$(echo "${branch}" | sed -e 's#^/##')" fi local repo="$(echo ${secondbase} | sed -e 's#\([^~]*\)~.*#\1#' -e 's#.*/##')" # Strip trailing trash introduced by erroneous inputs. local repo="$(echo ${repo} | sed -e 's#[[:punct:]]*$##')" else # no .git and no tilde for branches # Strip off any trailing @ sequences, even erroneous ones. local secondbase="$(echo "${noservice}" | sed -e "s#[^/]*/##" -e 's#@[[:alnum:]|@]*$##')" # If there's not .git then we can't possibly determine what's # a branch vs. what's part of the url vs. what's a repository. We # can only assume it's a repository. local branch= # The repo name is the content right of the rightmost / local repo="$(echo ${secondbase} | sed 's#.*/##')" fi # Strip trailing trash from the branch left by erroneous inputs. local branch="$(echo ${branch} | sed -e 's#[[:punct:]]*$##')" # The url is everything to the left of, and including the repo name itself. # Don't pick up any possibly superfluous @ information, and filter # out any tildes. #local url="$(echo ${in} | sed -n "s#\(.*${repo}\).*#\1#p" | sed -e 's#@[[:alnum:]|@]*$##')" local url="$(echo ${in} | sed -n "s#\(.*${repo}\).*#\1#p")" # Strip trailing @ symbols from the url. local url="$(echo ${url} | sed -e 's#@[[:alnum:]|@]*$##')" # Strip trailing trash from the url, except leave the http|git:// if test x"$(echo ${url} | grep -e "^${service}://")" != x; then local url="$(echo ${url} | sed -e "s#^${service}://##")" local url="$(echo ${url} | sed -e 's#[[:punct:]]*$##')" local url="${service}://${url}" else # If http|git:// isn't the last thing on the line # just clean up the trailing trash. local url="$(echo ${url} | sed -e 's#[[:punct:]]*$##')" fi if test x"${repo}" != x; then tool="$(echo ${repo} | sed -e "s#\.git##")" fi local validats=0 if test x"${revision}" != x; then validats="$(expr ${validats} + 1)" fi if test x"${user}" != x; then validats="$(expr ${validats} + 1)" fi local numats=0 # This counts the number of fields separated by the @ symbols numats=$(echo ${in} | awk -F "@" '{ print NF }') # Minus one is the number of @ symbols. numats="$(expr ${numats} - 1)" if test ${numats} -gt ${validats}; then superfluousats="$(expr ${numats} - ${validats})" error "Malformed input. Found ${superfluousats} superfluous '@' symbols. NUMATS: ${numats} VALIDATS: ${validats}" err=1 fi if test x"${url}" = x; then error "Malformed input. No url found." err=1 elif test x"${repo}" = x; then error "Malformed input. No repo found." err=1 fi case ${part} in service) echo "${service}" ;; user) echo "${user}" ;; tool) echo "${tool}" ;; url) echo "${url}" ;; repo) echo "${repo}" ;; branch) echo "${branch}" ;; revision) echo "${revision}" ;; *) error "Unknown part '${part}' requested from input string." err=1 ;; esac return ${err} } get_git_service() { local in=$1 local out= local ret= out="$(git_parser service ${in})" ret=$? echo "${out}" if test ${ret} -ne 0; then error "Malformed input \"${in}\"" fi return ${ret} } get_git_user() { local in=$1 local out= local ret= out="$(git_parser user ${in})" ret=$? echo "${out}" if test ${ret} -ne 0; then error "Malformed input \"${in}\"" fi return ${ret} } get_git_url() { local in=$1 local out= local ret= out="$(git_parser url ${in})" ret=$? echo "${out}" if test ${ret} -ne 0; then error "Malformed input \"${in}\"" fi return ${ret} } get_git_tool() { local in=$1 local out= local ret= out="$(git_parser tool ${in})" ret=$? echo "${out}" if test ${ret} -ne 0; then error "Malformed input \"${in}\"" fi return ${ret} } get_git_repo() { local in=$1 local out= local ret= out="$(git_parser repo ${in})" ret=$? echo "${out}" if test ${ret} -ne 0; then error "Malformed input \"${in}\"" fi return ${ret} } get_git_branch() { local in=$1 local out= local ret= out="$(git_parser branch ${in})" ret=$? echo "${out}" if test ${ret} -ne 0; then error "Malformed input \"${in}\"" fi return ${ret} } get_git_revision() { local in=$1 local out= local ret= out="$(git_parser revision ${in})" ret=$? echo "${out}" if test ${ret} -ne 0; then error "Malformed input \"${in}\"" fi return ${ret} } get_git_tag() { local in=$1 local ret= local out= local repo= local branch= local revision= repo="$(git_parser repo ${in})" ret=$? if test ${ret} -ne 0; then error "Malformed input \"${in}\"" return ${ret} fi if test x"${repo}" = x; then error "repository name required for meaningful response." return ${ret} fi branch="$(get_git_branch ${in})" || ( error "Malformed input \"${in}\""; return 1 ) # Multi-path branches should have forward slashes replaced with dashes. branch="$(echo ${branch} | sed 's:/:-:g')" revision="$(git_parser revision ${in})" || ( error "Malformed input \"${in}\""; return 1 ) echo "${repo}${branch:+~${branch}}${revision:+@${revision}}" return 0 }