[{"id":25401,"web_url":"https://patchwork.libcamera.org/comment/25401/","msgid":"<166558011217.3760285.17881484363826652332@Monstersaurus>","date":"2022-10-12T13:08:32","subject":"Re: [libcamera-devel] [PATCH v3 3/5] utils: semver: Add version\n\thelper","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Kieran Bingham (2022-10-10 18:32:12)\n> Provide the semver utility from [0] to make use of it with our\n> versioning and release scripts.\n> \n> [0] https://github.com/fsaintjacques/semver-tool/commit/cac039e8b73bc6fdf2f328437ed40f52abf0d05a\n\nTo be replaced directly with:\n\n https://raw.githubusercontent.com/fsaintjacques/semver-tool/3c76a6f9d113f4045f693845131185611a62162e/src/semver\n\nWhich now contains the updated SPDX header.\n\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> ---\n>  utils/semver | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 419 insertions(+)\n>  create mode 100755 utils/semver\n> \n> diff --git a/utils/semver b/utils/semver\n> new file mode 100755\n> index 000000000000..5b25f40ba48c\n> --- /dev/null\n> +++ b/utils/semver\n> @@ -0,0 +1,419 @@\n> +#!/usr/bin/env bash\n> +\n> +set -o errexit -o nounset -o pipefail\n> +\n> +NAT='0|[1-9][0-9]*'\n> +ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*'\n> +IDENT=\"$NAT|$ALPHANUM\"\n> +FIELD='[0-9A-Za-z-]+'\n> +\n> +SEMVER_REGEX=\"\\\n> +^[vV]?\\\n> +($NAT)\\\\.($NAT)\\\\.($NAT)\\\n> +(\\\\-(${IDENT})(\\\\.(${IDENT}))*)?\\\n> +(\\\\+${FIELD}(\\\\.${FIELD})*)?$\"\n> +\n> +PROG=semver\n> +PROG_VERSION=\"3.3.0\"\n> +\n> +USAGE=\"\\\n> +Usage:\n> +  $PROG bump (major|minor|patch|release|prerel [<prerel>]|build <build>) <version>\n> +  $PROG compare <version> <other_version>\n> +  $PROG diff <version> <other_version>\n> +  $PROG get (major|minor|patch|release|prerel|build) <version>\n> +  $PROG validate <version>\n> +  $PROG --help\n> +  $PROG --version\n> +\n> +Arguments:\n> +  <version>  A version must match the following regular expression:\n> +             \\\"${SEMVER_REGEX}\\\"\n> +             In English:\n> +             -- The version must match X.Y.Z[-PRERELEASE][+BUILD]\n> +                where X, Y and Z are non-negative integers.\n> +             -- PRERELEASE is a dot separated sequence of non-negative integers and/or\n> +                identifiers composed of alphanumeric characters and hyphens (with\n> +                at least one non-digit). Numeric identifiers must not have leading\n> +                zeros. A hyphen (\\\"-\\\") introduces this optional part.\n> +             -- BUILD is a dot separated sequence of identifiers composed of alphanumeric\n> +                characters and hyphens. A plus (\\\"+\\\") introduces this optional part.\n> +\n> +  <other_version>  See <version> definition.\n> +\n> +  <prerel>  A string as defined by PRERELEASE above. Or, it can be a PRERELEASE\n> +            prototype string (or empty) followed by a dot.\n> +\n> +  <build>   A string as defined by BUILD above.\n> +\n> +Options:\n> +  -v, --version          Print the version of this tool.\n> +  -h, --help             Print this help message.\n> +\n> +Commands:\n> +  bump      Bump by one of major, minor, patch; zeroing or removing\n> +            subsequent parts. \\\"bump prerel\\\" sets the PRERELEASE part and\n> +            removes any BUILD part. A trailing dot in the <prerel> argument\n> +            introduces an incrementing numeric field which is added or\n> +            bumped. If no <prerel> argument is provided, an incrementing numeric\n> +            field is introduced/bumped. \\\"bump build\\\" sets the BUILD part.\n> +            \\\"bump release\\\" removes any PRERELEASE or BUILD parts.\n> +            The bumped version is written to stdout.\n> +\n> +  compare   Compare <version> with <other_version>, output to stdout the\n> +            following values: -1 if <other_version> is newer, 0 if equal, 1 if\n> +            older. The BUILD part is not used in comparisons.\n> +\n> +  diff      Compare <version> with <other_version>, output to stdout the\n> +            difference between two versions by the release type (MAJOR, MINOR,\n> +            PATCH, PRERELEASE, BUILD).\n> +\n> +  get       Extract given part of <version>, where part is one of major, minor,\n> +            patch, prerel, build, or release.\n> +\n> +  validate  Validate if <version> follows the SEMVER pattern (see <version>\n> +            definition). Print 'valid' to stdout if the version is valid, otherwise\n> +            print 'invalid'.\n> +\n> +See also:\n> +  https://semver.org -- Semantic Versioning 2.0.0\"\n> +\n> +function error {\n> +  echo -e \"$1\" >&2\n> +  exit 1\n> +}\n> +\n> +function usage_help {\n> +  error \"$USAGE\"\n> +}\n> +\n> +function usage_version {\n> +  echo -e \"${PROG}: $PROG_VERSION\"\n> +  exit 0\n> +}\n> +\n> +function validate_version {\n> +  local version=$1\n> +  if [[ \"$version\" =~ $SEMVER_REGEX ]]; then\n> +    # if a second argument is passed, store the result in var named by $2\n> +    if [ \"$#\" -eq \"2\" ]; then\n> +      local major=${BASH_REMATCH[1]}\n> +      local minor=${BASH_REMATCH[2]}\n> +      local patch=${BASH_REMATCH[3]}\n> +      local prere=${BASH_REMATCH[4]}\n> +      local build=${BASH_REMATCH[8]}\n> +      eval \"$2=(\\\"$major\\\" \\\"$minor\\\" \\\"$patch\\\" \\\"$prere\\\" \\\"$build\\\")\"\n> +    else\n> +      echo \"$version\"\n> +    fi\n> +  else\n> +    error \"version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information.\"\n> +  fi\n> +}\n> +\n> +function is_nat {\n> +    [[ \"$1\" =~ ^($NAT)$ ]]\n> +}\n> +\n> +function is_null {\n> +    [ -z \"$1\" ]\n> +}\n> +\n> +function order_nat {\n> +    [ \"$1\" -lt \"$2\" ] && { echo -1 ; return ; }\n> +    [ \"$1\" -gt \"$2\" ] && { echo 1 ; return ; }\n> +    echo 0\n> +}\n> +\n> +function order_string {\n> +    [[ $1 < $2 ]] && { echo -1 ; return ; }\n> +    [[ $1 > $2 ]] && { echo 1 ; return ; }\n> +    echo 0\n> +}\n> +\n> +# given two (named) arrays containing NAT and/or ALPHANUM fields, compare them\n> +# one by one according to semver 2.0.0 spec. Return -1, 0, 1 if left array ($1)\n> +# is less-than, equal, or greater-than the right array ($2).  The longer array\n> +# is considered greater-than the shorter if the shorter is a prefix of the longer.\n> +#\n> +function compare_fields {\n> +    local l=\"$1[@]\"\n> +    local r=\"$2[@]\"\n> +    local leftfield=( \"${!l}\" )\n> +    local rightfield=( \"${!r}\" )\n> +    local left\n> +    local right\n> +\n> +    local i=$(( -1 ))\n> +    local order=$(( 0 ))\n> +\n> +    while true\n> +    do\n> +        [ $order -ne 0 ] && { echo $order ; return ; }\n> +\n> +        : $(( i++ ))\n> +        left=\"${leftfield[$i]}\"\n> +        right=\"${rightfield[$i]}\"\n> +\n> +        is_null \"$left\" && is_null \"$right\" && { echo 0  ; return ; }\n> +        is_null \"$left\"                     && { echo -1 ; return ; }\n> +                           is_null \"$right\" && { echo 1  ; return ; }\n> +\n> +        is_nat \"$left\" &&  is_nat \"$right\" && { order=$(order_nat \"$left\" \"$right\") ; continue ; }\n> +        is_nat \"$left\"                     && { echo -1 ; return ; }\n> +                           is_nat \"$right\" && { echo 1  ; return ; }\n> +                                              { order=$(order_string \"$left\" \"$right\") ; continue ; }\n> +    done\n> +}\n> +\n> +# shellcheck disable=SC2206     # checked by \"validate\"; ok to expand prerel id's into array\n> +function compare_version {\n> +  local order\n> +  validate_version \"$1\" V\n> +  validate_version \"$2\" V_\n> +\n> +  # compare major, minor, patch\n> +\n> +  local left=( \"${V[0]}\" \"${V[1]}\" \"${V[2]}\" )\n> +  local right=( \"${V_[0]}\" \"${V_[1]}\" \"${V_[2]}\" )\n> +\n> +  order=$(compare_fields left right)\n> +  [ \"$order\" -ne 0 ] && { echo \"$order\" ; return ; }\n> +\n> +  # compare pre-release ids when M.m.p are equal\n> +\n> +  local prerel=\"${V[3]:1}\"\n> +  local prerel_=\"${V_[3]:1}\"\n> +  local left=( ${prerel//./ } )\n> +  local right=( ${prerel_//./ } )\n> +\n> +  # if left and right have no pre-release part, then left equals right\n> +  # if only one of left/right has pre-release part, that one is less than simple M.m.p\n> +\n> +  [ -z \"$prerel\" ] && [ -z \"$prerel_\" ] && { echo 0  ; return ; }\n> +  [ -z \"$prerel\" ]                      && { echo 1  ; return ; }\n> +                      [ -z \"$prerel_\" ] && { echo -1 ; return ; }\n> +\n> +  # otherwise, compare the pre-release id's\n> +\n> +  compare_fields left right\n> +}\n> +\n> +# render_prerel -- return a prerel field with a trailing numeric string\n> +#                  usage: render_prerel numeric [prefix-string]\n> +#\n> +function render_prerel {\n> +    if [ -z \"$2\" ]\n> +    then\n> +        echo \"${1}\"\n> +    else\n> +        echo \"${2}${1}\"\n> +    fi\n> +}\n> +\n> +# extract_prerel -- extract prefix and trailing numeric portions of a pre-release part\n> +#                   usage: extract_prerel prerel prerel_parts\n> +#                   The prefix and trailing numeric parts are returned in \"prerel_parts\".\n> +#\n> +PREFIX_ALPHANUM='[.0-9A-Za-z-]*[.A-Za-z-]'\n> +DIGITS='[0-9][0-9]*'\n> +EXTRACT_REGEX=\"^(${PREFIX_ALPHANUM})*(${DIGITS})$\"\n> +\n> +function extract_prerel {\n> +    local prefix; local numeric;\n> +\n> +    if [[ \"$1\" =~ $EXTRACT_REGEX ]]\n> +    then                                        # found prefix and trailing numeric parts\n> +        prefix=\"${BASH_REMATCH[1]}\"\n> +        numeric=\"${BASH_REMATCH[2]}\"\n> +    else                                        # no numeric part\n> +        prefix=\"${1}\"\n> +        numeric=\n> +    fi\n> +\n> +    eval \"$2=(\\\"$prefix\\\" \\\"$numeric\\\")\"\n> +}\n> +\n> +# bump_prerel -- return the new pre-release part based on previous pre-release part\n> +#                and prototype for bump\n> +#                usage: bump_prerel proto previous\n> +#\n> +function bump_prerel {\n> +    local proto; local prev_prefix; local prev_numeric;\n> +\n> +    # case one: no trailing dot in prototype => simply replace previous with proto\n> +    if [[ ! ( \"$1\" =~ \\.$ ) ]]\n> +    then\n> +        echo \"$1\"\n> +        return\n> +    fi\n> +\n> +    proto=\"${1%.}\"                              # discard trailing dot marker from prototype\n> +\n> +    extract_prerel \"${2#-}\" prerel_parts        # extract parts of previous pre-release\n> +#   shellcheck disable=SC2154\n> +    prev_prefix=\"${prerel_parts[0]}\"\n> +    prev_numeric=\"${prerel_parts[1]}\"\n> +\n> +    # case two: bump or append numeric to previous pre-release part\n> +    if [ \"$proto\" == \"+\" ]                      # dummy \"+\" indicates no prototype argument provided\n> +    then\n> +        if [ -n \"$prev_numeric\" ]\n> +        then\n> +            : $(( ++prev_numeric ))             # previous pre-release is already numbered, bump it\n> +            render_prerel \"$prev_numeric\" \"$prev_prefix\"\n> +        else\n> +            render_prerel 1 \"$prev_prefix\"      # append starting number\n> +        fi\n> +        return\n> +    fi\n> +\n> +    # case three: set, bump, or append using prototype prefix\n> +    if [  \"$prev_prefix\" != \"$proto\" ]\n> +    then\n> +        render_prerel 1 \"$proto\"                # proto not same pre-release; set and start at '1'\n> +    elif [ -n \"$prev_numeric\" ]\n> +    then\n> +        : $(( ++prev_numeric ))                 # pre-release is numbered; bump it\n> +        render_prerel \"$prev_numeric\" \"$prev_prefix\"\n> +    else\n> +        render_prerel 1 \"$prev_prefix\"          # start pre-release at number '1'\n> +    fi\n> +}\n> +\n> +function command_bump {\n> +  local new; local version; local sub_version; local command;\n> +\n> +  case $# in\n> +    2) case $1 in\n> +        major|minor|patch|prerel|release) command=$1; sub_version=\"+.\"; version=$2;;\n> +        *) usage_help;;\n> +       esac ;;\n> +    3) case $1 in\n> +        prerel|build) command=$1; sub_version=$2 version=$3 ;;\n> +        *) usage_help;;\n> +       esac ;;\n> +    *) usage_help;;\n> +  esac\n> +\n> +  validate_version \"$version\" parts\n> +  # shellcheck disable=SC2154\n> +  local major=\"${parts[0]}\"\n> +  local minor=\"${parts[1]}\"\n> +  local patch=\"${parts[2]}\"\n> +  local prere=\"${parts[3]}\"\n> +  local build=\"${parts[4]}\"\n> +\n> +  case \"$command\" in\n> +    major) new=\"$((major + 1)).0.0\";;\n> +    minor) new=\"${major}.$((minor + 1)).0\";;\n> +    patch) new=\"${major}.${minor}.$((patch + 1))\";;\n> +    release) new=\"${major}.${minor}.${patch}\";;\n> +    prerel) new=$(validate_version \"${major}.${minor}.${patch}-$(bump_prerel \"$sub_version\" \"$prere\")\");;\n> +    build) new=$(validate_version \"${major}.${minor}.${patch}${prere}+${sub_version}\");;\n> +    *) usage_help ;;\n> +  esac\n> +\n> +  echo \"$new\"\n> +  exit 0\n> +}\n> +\n> +function command_compare {\n> +  local v; local v_;\n> +\n> +  case $# in\n> +    2) v=$(validate_version \"$1\"); v_=$(validate_version \"$2\") ;;\n> +    *) usage_help ;;\n> +  esac\n> +\n> +  set +u                        # need unset array element to evaluate to null\n> +  compare_version \"$v\" \"$v_\"\n> +  exit 0\n> +}\n> +\n> +function command_diff {\n> +  validate_version \"$1\" v1_parts\n> +  # shellcheck disable=SC2154\n> +  local v1_major=\"${v1_parts[0]}\"\n> +  local v1_minor=\"${v1_parts[1]}\"\n> +  local v1_patch=\"${v1_parts[2]}\"\n> +  local v1_prere=\"${v1_parts[3]}\"\n> +  local v1_build=\"${v1_parts[4]}\"\n> +\n> +  validate_version \"$2\" v2_parts\n> +  # shellcheck disable=SC2154\n> +  local v2_major=\"${v2_parts[0]}\"\n> +  local v2_minor=\"${v2_parts[1]}\"\n> +  local v2_patch=\"${v2_parts[2]}\"\n> +  local v2_prere=\"${v2_parts[3]}\"\n> +  local v2_build=\"${v2_parts[4]}\"\n> +\n> +  if [ \"${v1_major}\" != \"${v2_major}\" ]; then\n> +    echo \"major\"\n> +  elif [ \"${v1_minor}\" != \"${v2_minor}\" ]; then\n> +    echo \"minor\"\n> +  elif [ \"${v1_patch}\" != \"${v2_patch}\" ]; then\n> +    echo \"patch\"\n> +  elif [ \"${v1_prere}\" != \"${v2_prere}\" ]; then\n> +    echo \"prerelease\"\n> +  elif [ \"${v1_build}\" != \"${v2_build}\" ]; then\n> +    echo \"build\"\n> +  fi\n> +}\n> +\n> +# shellcheck disable=SC2034\n> +function command_get {\n> +    local part version\n> +\n> +    if [[ \"$#\" -ne \"2\" ]] || [[ -z \"$1\" ]] || [[ -z \"$2\" ]]; then\n> +        usage_help\n> +        exit 0\n> +    fi\n> +\n> +    part=\"$1\"\n> +    version=\"$2\"\n> +\n> +    validate_version \"$version\" parts\n> +    local major=\"${parts[0]}\"\n> +    local minor=\"${parts[1]}\"\n> +    local patch=\"${parts[2]}\"\n> +    local prerel=\"${parts[3]:1}\"\n> +    local build=\"${parts[4]:1}\"\n> +    local release=\"${major}.${minor}.${patch}\"\n> +\n> +    case \"$part\" in\n> +        major|minor|patch|release|prerel|build) echo \"${!part}\" ;;\n> +        *) usage_help ;;\n> +    esac\n> +\n> +    exit 0\n> +}\n> +\n> +function command_validate {\n> +  if [[ \"$#\" -ne \"1\" ]]; then\n> +        usage_help\n> +  fi  \n> +  \n> +  if [[ \"$1\" =~ $SEMVER_REGEX ]]; then\n> +    echo \"valid\"\n> +  else\n> +    echo \"invalid\"\n> +  fi\n> +\n> +  exit 0\n> +}\n> +\n> +case $# in\n> +  0) echo \"Unknown command: $*\"; usage_help;;\n> +esac\n> +\n> +case $1 in\n> +  --help|-h) echo -e \"$USAGE\"; exit 0;;\n> +  --version|-v) usage_version ;;\n> +  bump) shift; command_bump \"$@\";;\n> +  get) shift; command_get \"$@\";;\n> +  compare) shift; command_compare \"$@\";;\n> +  diff) shift; command_diff \"$@\";;\n> +  validate) shift; command_validate \"$@\";;\n> +  *) echo \"Unknown arguments: $*\"; usage_help;;\n> +esac\n> -- \n> 2.34.1\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 4D093BD16B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 12 Oct 2022 13:08:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0380962D9A;\n\tWed, 12 Oct 2022 15:08:37 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D9615603D7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 12 Oct 2022 15:08:34 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 778B34D3;\n\tWed, 12 Oct 2022 15:08:34 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1665580117;\n\tbh=ajUV6jMKc2R+EqL619SESASM3tj8SU9f+Vgf3tSE/ug=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=YXSieXOCwR2Eu5n+ryWrSA8MDf6D3UHaK4T6WsfbgKyT8LiYFVzOuR636Ble2XebD\n\tdZVxs5ZH94TYfW7xxK1O4hN62SNbyzfjYop7BzE4xduLpfNb6PGcG3Asb1kpj5H2Ag\n\tbc19z5aAkdSvt+Rnw3TIHwVdhd447sZVFzkcrR+FvLJ9CMaRI9Kb5tetIicU2xJXjy\n\tKT7E+NoiWbZ9Bp1466R9tOWt0qyZNdlkYdhJfa/pfGC6HMB61whIVE/qhz5aa5XYb5\n\toO/reipWJKEsthIFenehwYinJrpRPeiUdYd2QgTh0VImTtgCgqQX9URgzXZI9D8Chr\n\tT9jax9ZHaO6FQ==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1665580114;\n\tbh=ajUV6jMKc2R+EqL619SESASM3tj8SU9f+Vgf3tSE/ug=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=CDkdCjkNVNHa5qG5fy4U3BXIk9SCfelpw4vt59M9GbS3Q8lfIRZ2zoSpYljXMqROc\n\tG+SqSHZ8uqTheOMj75bArBBNz9ecYRVjpk6MTsa9uXL8CRW/QM/kk8GHjeC+vdqZW+\n\tif6tCfPVuln8Virx60321eZDUpK0qnzvh79FTiWA="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"CDkdCjkN\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20221010173214.3547133-4-kieran.bingham@ideasonboard.com>","References":"<20221010173214.3547133-1-kieran.bingham@ideasonboard.com>\n\t<20221010173214.3547133-4-kieran.bingham@ideasonboard.com>","To":"libcamera devel <libcamera-devel@lists.libcamera.org>","Date":"Wed, 12 Oct 2022 14:08:32 +0100","Message-ID":"<166558011217.3760285.17881484363826652332@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH v3 3/5] utils: semver: Add version\n\thelper","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]