From patchwork Thu Jan 7 01:06:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Bruno Haible X-Patchwork-Id: 41657 X-Patchwork-Delegate: azanella@linux.vnet.ibm.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D0017384388B; Thu, 7 Jan 2021 01:07:11 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mo4-p00-ob.smtp.rzone.de (mo4-p00-ob.smtp.rzone.de [85.215.255.23]) by sourceware.org (Postfix) with ESMTPS id 321C23865470 for ; Thu, 7 Jan 2021 01:07:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 321C23865470 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=clisp.org Authentication-Results: sourceware.org; spf=none smtp.mailfrom=bruno@clisp.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1609981629; s=strato-dkim-0002; d=clisp.org; h=References:In-Reply-To:Message-Id:Date:Subject:To:From:From:Subject: Sender; bh=ek8zw6O1AxwTaxHg8Y9YZVljm0EBcdE3KGICOvyN308=; b=Ugp6JmlMtvuS7lcsc0nWajol98Os/KK60SAVjLTcEINE0EkPrVdMQ0RLkHOGqerRyS zzpOoYybByYcZVFEb0FP/mGIuRzTLuwc/Drsjw5rr6kAkb85zkzJKJrY7u9DrA3V20c8 hGOuV7WnOgeZdsYJPrBB+nljjywHga6KldNBuQR31uuxmivBHfCQRAGXsCWORzxOg2cj DtaIJLiqnXZnatbAj4B52NRFNoflm05PUaCfRiqf3aaN27vunC0mgRNnEUC1+H5xHqSh s7jMpChFO2O/F7ofWM+y22SnTJnAU6Iel1yw9PzO88Arer5QC//S+UVffS2p6eZL5WYM p4Uw== X-RZG-AUTH: ":Ln4Re0+Ic/6oZXR1YgKryK8brlshOcZlIWs+iCP5vnk6shH+AHjwLuWOHqf3yZdW" X-RZG-CLASS-ID: mo00 Received: from omega.bruno.haible.de by smtp.strato.de (RZmta 47.12.1 DYNA|AUTH) with ESMTPSA id u0aa20x0717231r (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (curve X9_62_prime256v1 with 256 ECDH bits, eq. 3072 bits RSA)) (Client did not present a certificate); Thu, 7 Jan 2021 02:07:02 +0100 (CET) From: Bruno Haible To: libc-alpha@sourceware.org, Paul Eggert Subject: [PATCH 1/5] argp: fix pointer-subtraction bug Date: Thu, 7 Jan 2021 02:06:16 +0100 Message-Id: <1609981580-17229-2-git-send-email-bruno@clisp.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1609981580-17229-1-git-send-email-bruno@clisp.org> References: <1609981580-17229-1-git-send-email-bruno@clisp.org> MIME-Version: 1.0 X-Spam-Status: No, score=-10.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" From: Paul Eggert * argp/argp-help.c (hol_append): Don’t subtract pointers to different arrays, as this can run afoul of -fcheck-pointer-bounds. See the thread containing Bruno Haible’s report in: http://lists.gnu.org/archive/html/bug-gnulib/2017-05/msg00171.html Reviewed-by: Adhemerval Zanella --- argp/argp-help.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/argp/argp-help.c b/argp/argp-help.c index 15c5fd2..f417e12 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -867,7 +867,8 @@ hol_append (struct hol *hol, struct hol *more) /* Fix up the short options pointers from HOL. */ for (e = entries, left = hol->num_entries; left > 0; e++, left--) - e->short_options += (short_options - hol->short_options); + e->short_options + = short_options + (e->short_options - hol->short_options); /* Now add the short options from MORE, fixing up its entries too. */ From patchwork Thu Jan 7 01:06:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bruno Haible X-Patchwork-Id: 41659 X-Patchwork-Delegate: azanella@linux.vnet.ibm.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A1FA83948451; Thu, 7 Jan 2021 01:07:13 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mo4-p01-ob.smtp.rzone.de (mo4-p01-ob.smtp.rzone.de [81.169.146.167]) by sourceware.org (Postfix) with ESMTPS id 41C5D3844072 for ; Thu, 7 Jan 2021 01:07:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 41C5D3844072 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=clisp.org Authentication-Results: sourceware.org; spf=none smtp.mailfrom=bruno@clisp.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1609981630; s=strato-dkim-0002; d=clisp.org; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:From: Subject:Sender; bh=DLlL7D2sCFv2pYJkNc93yIbaO7ZXkJOEyJJ6JhAhkdU=; b=jzm54SXvQo/J1wOt2lnWjcFTOWTvbNbgIFNZFU2r+q3f2nnZM5vyGaQ7lZ+iVCjtcK +Ypv5YDndxJiCBlf7cdOKppL/X0/eF2Fifk+ShGif+xaAcmgucbPIAvhWM5Sc36vDyrd PzaG5AP9Er8NEd9mEp3AZpxUcVgAtP4bFWWcBbqaMXNqZKju5fDTlNe2PYdFO3RjCPd3 J7mL3gllrBCuLW5Pz6iprNroLRErBv67CceV+QmqNdwDxov/+LM47WJmlVKzzNm7vRZB ksDnj/vHpyynpgZIcef9g6C1VJ9RDVzgMg3t5NvW3WNPSwM1AwiMasnzAOfxxnVDMyKd 2OvQ== X-RZG-AUTH: ":Ln4Re0+Ic/6oZXR1YgKryK8brlshOcZlIWs+iCP5vnk6shH+AHjwLuWOHqf3yZdW" X-RZG-CLASS-ID: mo00 Received: from omega.bruno.haible.de by smtp.strato.de (RZmta 47.12.1 DYNA|AUTH) with ESMTPSA id u0aa20x0717231s (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (curve X9_62_prime256v1 with 256 ECDH bits, eq. 3072 bits RSA)) (Client did not present a certificate); Thu, 7 Jan 2021 02:07:02 +0100 (CET) From: Bruno Haible To: libc-alpha@sourceware.org, Paul Eggert Subject: [PATCH 2/5] argp: Don't rely on undefined behaviour of _tolower(). Date: Thu, 7 Jan 2021 02:06:17 +0100 Message-Id: <1609981580-17229-3-git-send-email-bruno@clisp.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1609981580-17229-1-git-send-email-bruno@clisp.org> References: <1609981580-17229-1-git-send-email-bruno@clisp.org> X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Bruno Haible Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" Patch by Eric Blake . * argp/argp-help.c (hol_entry_cmp): Don't use _tolower on values that are not upper-case. Pass correct range to tolower. Reviewed-by: Adhemerval Zanella --- argp/argp-help.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/argp/argp-help.c b/argp/argp-help.c index f417e12..5844d5b 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -780,13 +780,11 @@ hol_entry_cmp (const struct hol_entry *entry1, first, but as they're not displayed, it doesn't matter where they are. */ { - char first1 = short1 ? short1 : long1 ? *long1 : 0; - char first2 = short2 ? short2 : long2 ? *long2 : 0; -#ifdef _tolower - int lower_cmp = _tolower (first1) - _tolower (first2); -#else + unsigned char first1 = short1 ? short1 : long1 ? *long1 : 0; + unsigned char first2 = short2 ? short2 : long2 ? *long2 : 0; + /* Use tolower, not _tolower, since the latter has undefined + behaviour for characters that are not uppercase letters. */ int lower_cmp = tolower (first1) - tolower (first2); -#endif /* Compare ignoring case, except when the options are both the same letter, in which case lower-case always comes first. */ return lower_cmp ? lower_cmp : first2 - first1; From patchwork Thu Jan 7 01:06:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bruno Haible X-Patchwork-Id: 41658 X-Patchwork-Delegate: azanella@linux.vnet.ibm.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3DD60388E82E; Thu, 7 Jan 2021 01:07:13 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mo4-p01-ob.smtp.rzone.de (mo4-p01-ob.smtp.rzone.de [85.215.255.53]) by sourceware.org (Postfix) with ESMTPS id 3E4FA3846033 for ; Thu, 7 Jan 2021 01:07:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 3E4FA3846033 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=clisp.org Authentication-Results: sourceware.org; spf=none smtp.mailfrom=bruno@clisp.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1609981630; s=strato-dkim-0002; d=clisp.org; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:From: Subject:Sender; bh=yFQtna7esoFOWH/fSy3xZ5SBLN81XuGcja70cDkS7sI=; b=fMWBccLdBunRCRKgRlbe85vzUOEpwutN0/GN9lc1Tg0hGslQ1Jm9y1ZDU934nfYRgg 6YqkG2JOcZVdWcEp7Q7P6btLo2YVWoNxAxHfQ3mlz/VeYWdArXtP4Y7tEaUYDV3yW+P0 tdQCHH2Arda2Skqq0fFj3kq5y6NNctd4j6KftQcJdvmcvMKxnhtQ0uC0knfhPVd1hJH4 angpYIFoG42t8EY8rmuJ0IEO4riZFGzr8xZtp9UyrHZsOmEw5cOuMXVDAvr4fDEyNm8C jcnW4pdIZs3OikVRanvapeszxNL8AJgk6gxwldH7TT4YjNZulvfCVmLCEt0SXRvB1wWV OF7g== X-RZG-AUTH: ":Ln4Re0+Ic/6oZXR1YgKryK8brlshOcZlIWs+iCP5vnk6shH+AHjwLuWOHqf3yZdW" X-RZG-CLASS-ID: mo00 Received: from omega.bruno.haible.de by smtp.strato.de (RZmta 47.12.1 DYNA|AUTH) with ESMTPSA id u0aa20x0717231t (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (curve X9_62_prime256v1 with 256 ECDH bits, eq. 3072 bits RSA)) (Client did not present a certificate); Thu, 7 Jan 2021 02:07:02 +0100 (CET) From: Bruno Haible To: libc-alpha@sourceware.org, Paul Eggert Subject: [PATCH 3/5] argp: Don't pass invalid arguments to isspace, isalnum, isalpha, isdigit. Date: Thu, 7 Jan 2021 02:06:18 +0100 Message-Id: <1609981580-17229-4-git-send-email-bruno@clisp.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1609981580-17229-1-git-send-email-bruno@clisp.org> References: <1609981580-17229-1-git-send-email-bruno@clisp.org> X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Bruno Haible Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" * lib/argp-help.c (SKIPWS): Cast character to 'unsigned char' before passing it to isspace(). (fill_in_uparams): Likewise for isalpha(), isalnum(), isdigit(). (canon_doc_option): Likewise for isspace(), isalnum(). Reviewed-by: Adhemerval Zanella --- argp/argp-help.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/argp/argp-help.c b/argp/argp-help.c index 5844d5b..d686a23 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -166,7 +166,7 @@ fill_in_uparams (const struct argp_state *state) { const char *var = getenv ("ARGP_HELP_FMT"); -#define SKIPWS(p) do { while (isspace (*p)) p++; } while (0); +#define SKIPWS(p) do { while (isspace ((unsigned char) *p)) p++; } while (0); if (var) /* Parse var. */ @@ -174,14 +174,14 @@ fill_in_uparams (const struct argp_state *state) { SKIPWS (var); - if (isalpha (*var)) + if (isalpha ((unsigned char) *var)) { size_t var_len; const struct uparam_name *un; int unspec = 0, val = 0; const char *arg = var; - while (isalnum (*arg) || *arg == '-' || *arg == '_') + while (isalnum ((unsigned char) *arg) || *arg == '-' || *arg == '_') arg++; var_len = arg - var; @@ -206,10 +206,10 @@ fill_in_uparams (const struct argp_state *state) else val = 1; } - else if (isdigit (*arg)) + else if (isdigit ((unsigned char) *arg)) { val = atoi (arg); - while (isdigit (*arg)) + while (isdigit ((unsigned char) *arg)) arg++; SKIPWS (arg); } @@ -713,12 +713,12 @@ canon_doc_option (const char **name) { int non_opt; /* Skip initial whitespace. */ - while (isspace (**name)) + while (isspace ((unsigned char) **name)) (*name)++; /* Decide whether this looks like an option (leading `-') or not. */ non_opt = (**name != '-'); /* Skip until part of name used for sorting. */ - while (**name && !isalnum (**name)) + while (**name && !isalnum ((unsigned char) **name)) (*name)++; return non_opt; } From patchwork Thu Jan 7 01:06:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bruno Haible X-Patchwork-Id: 41661 X-Patchwork-Delegate: azanella@linux.vnet.ibm.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 058A439524B0; Thu, 7 Jan 2021 01:07:19 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mo4-p01-ob.smtp.rzone.de (mo4-p01-ob.smtp.rzone.de [81.169.146.164]) by sourceware.org (Postfix) with ESMTPS id 3398B3870857 for ; Thu, 7 Jan 2021 01:07:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 3398B3870857 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=clisp.org Authentication-Results: sourceware.org; spf=none smtp.mailfrom=bruno@clisp.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1609981635; s=strato-dkim-0002; d=clisp.org; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:From: Subject:Sender; bh=uYlpoPGzJYTiVx2UDh8QyWYb73Wrjqlo4Ah+PklxFUQ=; b=aZYp7HxgopP0cXXL0lcafzECbaQDDrv/2o3GN+fAxiivpyK/rRJrE1600jp0E7VT/y 1W5ObFAtKKCdU+G8zlpgnP9eq9x3lShY1513HU75ROOO4wyvfHSYxHT+eN/Ef8oG0kUQ QuES6NcQg+6B5YZ9nLS/lYfPCLiAXkj2rDPogxaEqzRZIy5MCT68kTFD3OD8klPV9Nuw aJJpYjCGDsWb1rFZ3U2SWtUPmEpMKJqwa7LIl5IBGgMBLC1k2satxYwtuGuvx7m3ObPo q46/cQmkdtzdblzagfqAG0n2+BtMTsjBjayUyveaco4AZLSkGUm5vWMjhj1nWgKP4BV0 d9zA== X-RZG-AUTH: ":Ln4Re0+Ic/6oZXR1YgKryK8brlshOcZlIWs+iCP5vnk6shH+AHjwLuWOHqf3yZdW" X-RZG-CLASS-ID: mo00 Received: from omega.bruno.haible.de by smtp.strato.de (RZmta 47.12.1 DYNA|AUTH) with ESMTPSA id u0aa20x0717331u (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (curve X9_62_prime256v1 with 256 ECDH bits, eq. 3072 bits RSA)) (Client did not present a certificate); Thu, 7 Jan 2021 02:07:03 +0100 (CET) From: Bruno Haible To: libc-alpha@sourceware.org, Paul Eggert Subject: [PATCH 4/5] argp: Improve comments. Date: Thu, 7 Jan 2021 02:06:19 +0100 Message-Id: <1609981580-17229-5-git-send-email-bruno@clisp.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1609981580-17229-1-git-send-email-bruno@clisp.org> References: <1609981580-17229-1-git-send-email-bruno@clisp.org> X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Bruno Haible Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" * argp/argp-help.c: Add sectioning comments. Write NULL to designate a null pointer. (struct hol_entry): Fix comment regarding sort order of group. (hol_entry_short_iterate, hol_entry_long_iterate): Add comment. (until_short, canon_doc_option, hol_entry_qcmp): Improve comment. (hol_cluster_is_child, argp_hol): Move functions. Reviewed-by: Adhemerval Zanella --- argp/argp-help.c | 108 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/argp/argp-help.c b/argp/argp-help.c index d686a23..637abca 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -87,6 +87,8 @@ char *strerror (int errnum); # define SIZE_MAX ((size_t) -1) #endif +/* ========================================================================== */ + /* User-selectable (using an environment variable) formatting parameters. These may be specified in an environment variable called `ARGP_HELP_FMT', @@ -253,6 +255,8 @@ fill_in_uparams (const struct argp_state *state) } } +/* ========================================================================== */ + /* Returns true if OPT hasn't been marked invisible. Visibility only affects whether OPT is displayed or used in sorting, not option shadowing. */ #define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN)) @@ -345,6 +349,9 @@ find_char (char ch, char *beg, char *end) return 0; } +/* -------------------------------------------------------------------------- */ +/* Data structure: HOL = Help Option List */ + struct hol_cluster; /* fwd decl */ struct hol_entry @@ -363,11 +370,11 @@ struct hol_entry char *short_options; /* Entries are sorted by their group first, in the order: - 1, 2, ..., n, 0, -m, ..., -2, -1 + 0, 1, 2, ..., n, -m, ..., -2, -1 and then alphabetically within each group. The default is 0. */ int group; - /* The cluster of options this entry belongs to, or 0 if none. */ + /* The cluster of options this entry belongs to, or NULL if none. */ struct hol_cluster *cluster; /* The argp from which this option came. */ @@ -389,7 +396,7 @@ struct hol_cluster same depth (clusters always follow options in the same group). */ int group; - /* The cluster to which this cluster belongs, or 0 if it's at the base + /* The cluster to which this cluster belongs, or NULL if it's at the base level. */ struct hol_cluster *parent; @@ -422,7 +429,7 @@ struct hol }; /* Create a struct hol from the options in ARGP. CLUSTER is the - hol_cluster in which these entries occur, or 0, if at the root. */ + hol_cluster in which these entries occur, or NULL if at the root. */ static struct hol * make_hol (const struct argp *argp, struct hol_cluster *cluster) { @@ -540,6 +547,9 @@ hol_free (struct hol *hol) free (hol); } +/* Iterate across the short_options of the given ENTRY. Call FUNC for each. + Stop when such a call returns a non-zero value, and return this value. + If all FUNC invocations returned 0, return 0. */ static int hol_entry_short_iterate (const struct hol_entry *entry, int (*func)(const struct argp_option *opt, @@ -565,6 +575,9 @@ hol_entry_short_iterate (const struct hol_entry *entry, return val; } +/* Iterate across the long options of the given ENTRY. Call FUNC for each. + Stop when such a call returns a non-zero value, and return this value. + If all FUNC invocations returned 0, return 0. */ static inline int __attribute__ ((always_inline)) hol_entry_long_iterate (const struct hol_entry *entry, @@ -589,7 +602,7 @@ hol_entry_long_iterate (const struct hol_entry *entry, return val; } -/* Iterator that returns true for the first short option. */ +/* A filter that returns true for the first short option of a given ENTRY. */ static inline int until_short (const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie) @@ -605,7 +618,7 @@ hol_entry_first_short (const struct hol_entry *entry) entry->argp->argp_domain, 0); } -/* Returns the first valid long option in ENTRY, or 0 if there is none. */ +/* Returns the first valid long option in ENTRY, or NULL if there is none. */ static const char * hol_entry_first_long (const struct hol_entry *entry) { @@ -617,7 +630,7 @@ hol_entry_first_long (const struct hol_entry *entry) return 0; } -/* Returns the entry in HOL with the long option name NAME, or 0 if there is +/* Returns the entry in HOL with the long option name NAME, or NULL if there is none. */ static struct hol_entry * hol_find_entry (struct hol *hol, const char *name) @@ -652,6 +665,9 @@ hol_set_group (struct hol *hol, const char *name, int group) entry->group = group; } +/* -------------------------------------------------------------------------- */ +/* Sorting the entries in a HOL. */ + /* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. EQ is what to return if GROUP1 and GROUP2 are the same. */ static int @@ -694,18 +710,8 @@ hol_cluster_base (struct hol_cluster *cl) cl = cl->parent; return cl; } - -/* Return true if CL1 is a child of CL2. */ -static int -hol_cluster_is_child (const struct hol_cluster *cl1, - const struct hol_cluster *cl2) -{ - while (cl1 && cl1 != cl2) - cl1 = cl1->parent; - return cl1 == cl2; -} -/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail +/* Given the name of an OPTION_DOC option, modifies *NAME to start at the tail that should be used for comparisons, and returns true iff it should be treated as a non-option. */ static int @@ -796,7 +802,7 @@ hol_entry_cmp (const struct hol_entry *entry1, return group_cmp (group1, group2, 0); } -/* Version of hol_entry_cmp with correct signature for qsort. */ +/* Variant of hol_entry_cmp with correct signature for qsort. */ static int hol_entry_qcmp (const void *entry1_v, const void *entry2_v) { @@ -814,6 +820,9 @@ hol_sort (struct hol *hol) hol_entry_qcmp); } +/* -------------------------------------------------------------------------- */ +/* Constructing the HOL. */ + /* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow any in MORE with the same name. */ static void @@ -909,6 +918,32 @@ hol_append (struct hol *hol, struct hol *more) hol_free (more); } +/* Make a HOL containing all levels of options in ARGP. CLUSTER is the + cluster in which ARGP's entries should be clustered, or 0. */ +static struct hol * +argp_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + const struct argp_child *child = argp->children; + struct hol *hol = make_hol (argp, cluster); + if (child) + while (child->argp) + { + struct hol_cluster *child_cluster = + ((child->group || child->header) + /* Put CHILD->argp within its own cluster. */ + ? hol_add_cluster (hol, child->group, child->header, + child - argp->children, cluster, argp) + /* Just merge it into the parent's cluster. */ + : cluster); + hol_append (hol, argp_hol (child->argp, child_cluster)) ; + child++; + } + return hol; +} + +/* -------------------------------------------------------------------------- */ +/* Printing the HOL. */ + /* Inserts enough spaces to make sure STREAM is at column COL. */ static void indent_to (argp_fmtstream_t stream, unsigned col) @@ -953,7 +988,7 @@ arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt, /* State used during the execution of hol_help. */ struct hol_help_state { - /* PREV_ENTRY should contain the previous entry printed, or 0. */ + /* PREV_ENTRY should contain the previous entry printed, or NULL. */ struct hol_entry *prev_entry; /* If an entry is in a different group from the previous one, and SEP_GROUPS @@ -1031,6 +1066,16 @@ print_header (const char *str, const struct argp *argp, free ((char *) fstr); } +/* Return true if CL1 is a child of CL2. */ +static int +hol_cluster_is_child (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + while (cl1 && cl1 != cl2) + cl1 = cl1->parent; + return cl1 == cl2; +} + /* Inserts a comma if this isn't the first item on the line, and then makes sure we're at least to column COL. If this *is* the first item on a line, prints any pending whitespace/headers that should precede this line. Also @@ -1344,29 +1389,6 @@ hol_usage (struct hol *hol, argp_fmtstream_t stream) } } -/* Make a HOL containing all levels of options in ARGP. CLUSTER is the - cluster in which ARGP's entries should be clustered, or 0. */ -static struct hol * -argp_hol (const struct argp *argp, struct hol_cluster *cluster) -{ - const struct argp_child *child = argp->children; - struct hol *hol = make_hol (argp, cluster); - if (child) - while (child->argp) - { - struct hol_cluster *child_cluster = - ((child->group || child->header) - /* Put CHILD->argp within its own cluster. */ - ? hol_add_cluster (hol, child->group, child->header, - child - argp->children, cluster, argp) - /* Just merge it into the parent's cluster. */ - : cluster); - hol_append (hol, argp_hol (child->argp, child_cluster)) ; - child++; - } - return hol; -} - /* Calculate how many different levels with alternative args strings exist in ARGP. */ static size_t From patchwork Thu Jan 7 01:06:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bruno Haible X-Patchwork-Id: 41660 X-Patchwork-Delegate: azanella@linux.vnet.ibm.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 08C953844072; Thu, 7 Jan 2021 01:07:16 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mo4-p01-ob.smtp.rzone.de (mo4-p01-ob.smtp.rzone.de [81.169.146.165]) by sourceware.org (Postfix) with ESMTPS id 85A513870857 for ; Thu, 7 Jan 2021 01:07:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 85A513870857 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=clisp.org Authentication-Results: sourceware.org; spf=none smtp.mailfrom=bruno@clisp.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1609981631; s=strato-dkim-0002; d=clisp.org; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:From: Subject:Sender; bh=MMFfIK6iRRJyl4rFu6mgUAXW+O+L83hk48S5/BiD6CY=; b=DtZB+d90IgOK7F6VrvdIW0Jv1nRJSSjv3I91kf0WCIW4HNtjcVIn2X44TGmRyGlgJP ADcAzVHQGda0KWegWk2YviE/1KRcKWj1/9OYSo/oakocyBiATvTDI4wB6BAJErvuOB1q 4HSKvxu7eBzkkGQNyHiKC5H++InzloPBBPTHRn2ZSPHRZEwcF9NcPmBo/BQCoQ5aI+Zl e/VYahZ4BJf0kULrWVT2D+OFJEF4e2t/cPtjxN8OuOHrqVH9siS53rlDx6CkKv842EQg fMBxI0R4zLVVyzrgytW0uYgooOM4q2bmhGlwToe+5jkw9P+8IZqQQ+65KnuPsyroO/uS 6kZA== X-RZG-AUTH: ":Ln4Re0+Ic/6oZXR1YgKryK8brlshOcZlIWs+iCP5vnk6shH+AHjwLuWOHqf3yZdW" X-RZG-CLASS-ID: mo00 Received: from omega.bruno.haible.de by smtp.strato.de (RZmta 47.12.1 DYNA|AUTH) with ESMTPSA id u0aa20x0717331v (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (curve X9_62_prime256v1 with 256 ECDH bits, eq. 3072 bits RSA)) (Client did not present a certificate); Thu, 7 Jan 2021 02:07:03 +0100 (CET) From: Bruno Haible To: libc-alpha@sourceware.org, Paul Eggert Subject: [PATCH 5/5] argp: Avoid undefined behaviour when invoking qsort(). Date: Thu, 7 Jan 2021 02:06:20 +0100 Message-Id: <1609981580-17229-6-git-send-email-bruno@clisp.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1609981580-17229-1-git-send-email-bruno@clisp.org> References: <1609981580-17229-1-git-send-email-bruno@clisp.org> X-Spam-Status: No, score=-12.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Bruno Haible Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" This fixes a Gnulib test-argp-2.sh test failure on macOS and FreeBSD. Reported by Jeffrey Walton in . * argp/argp-help.c (group_cmp): Remove third argument. (hol_sibling_cluster_cmp, hol_cousin_cluster_cmp): New functions, based upon hol_cluster_cmp. (hol_cluster_cmp): Use hol_cousin_cluster_cmp. (hol_entry_cmp): Rewritten to implement a total order. Reviewed-by: Adhemerval Zanella --- argp/argp-help.c | 254 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 173 insertions(+), 81 deletions(-) diff --git a/argp/argp-help.c b/argp/argp-help.c index 637abca..02afba2 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -668,37 +668,90 @@ hol_set_group (struct hol *hol, const char *name, int group) /* -------------------------------------------------------------------------- */ /* Sorting the entries in a HOL. */ -/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. - EQ is what to return if GROUP1 and GROUP2 are the same. */ +/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. */ static int -group_cmp (int group1, int group2, int eq) +group_cmp (int group1, int group2) { - if (group1 == group2) - return eq; - else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) + if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) return group1 - group2; else + /* Return > 0 if group1 < 0 <= group2. + Return < 0 if group2 < 0 <= group1. */ return group2 - group1; } -/* Compare clusters CL1 & CL2 by the order that they should appear in +/* Compare clusters CL1 and CL2 by the order that they should appear in + output. Assume CL1 and CL2 have the same parent. */ +static int +hol_sibling_cluster_cmp (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + /* Compare by group first. */ + int cmp = group_cmp (cl1->group, cl2->group); + if (cmp != 0) + return cmp; + + /* Within a group, compare by index within the group. */ + return cl2->index - cl1->index; +} + +/* Compare clusters CL1 and CL2 by the order that they should appear in + output. Assume CL1 and CL2 are at the same depth. */ +static int +hol_cousin_cluster_cmp (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + if (cl1->parent == cl2->parent) + return hol_sibling_cluster_cmp (cl1, cl2); + else + { + /* Compare the parent clusters first. */ + int cmp = hol_cousin_cluster_cmp (cl1->parent, cl2->parent); + if (cmp != 0) + return cmp; + + /* Next, compare by group. */ + cmp = group_cmp (cl1->group, cl2->group); + if (cmp != 0) + return cmp; + + /* Next, within a group, compare by index within the group. */ + return cl2->index - cl1->index; + } +} + +/* Compare clusters CL1 and CL2 by the order that they should appear in output. */ static int hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2) { /* If one cluster is deeper than the other, use its ancestor at the same - level, so that finding the common ancestor is straightforward. */ - while (cl1->depth > cl2->depth) - cl1 = cl1->parent; - while (cl2->depth > cl1->depth) - cl2 = cl2->parent; + level. Then, go by the rule that entries that are not in a sub-cluster + come before entries in a sub-cluster. */ + if (cl1->depth > cl2->depth) + { + do + cl1 = cl1->parent; + while (cl1->depth > cl2->depth); + int cmp = hol_cousin_cluster_cmp (cl1, cl2); + if (cmp != 0) + return cmp; - /* Now reduce both clusters to their ancestors at the point where both have - a common parent; these can be directly compared. */ - while (cl1->parent != cl2->parent) - cl1 = cl1->parent, cl2 = cl2->parent; + return 1; + } + else if (cl1->depth < cl2->depth) + { + do + cl2 = cl2->parent; + while (cl1->depth < cl2->depth); + int cmp = hol_cousin_cluster_cmp (cl1, cl2); + if (cmp != 0) + return cmp; - return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index); + return -1; + } + else + return hol_cousin_cluster_cmp (cl1, cl2); } /* Return the ancestor of CL that's just below the root (i.e., has a parent @@ -710,7 +763,7 @@ hol_cluster_base (struct hol_cluster *cl) cl = cl->parent; return cl; } - + /* Given the name of an OPTION_DOC option, modifies *NAME to start at the tail that should be used for comparisons, and returns true iff it should be treated as a non-option. */ @@ -721,7 +774,7 @@ canon_doc_option (const char **name) /* Skip initial whitespace. */ while (isspace ((unsigned char) **name)) (*name)++; - /* Decide whether this looks like an option (leading `-') or not. */ + /* Decide whether this looks like an option (leading '-') or not. */ non_opt = (**name != '-'); /* Skip until part of name used for sorting. */ while (**name && !isalnum ((unsigned char) **name)) @@ -729,77 +782,116 @@ canon_doc_option (const char **name) return non_opt; } -/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help - listing. */ +/* Order ENTRY1 and ENTRY2 by the order which they should appear in a help + listing. + This function implements a total order, that is: + - if cmp (entry1, entry2) < 0 and cmp (entry2, entry3) < 0, + then cmp (entry1, entry3) < 0. + - if cmp (entry1, entry2) < 0 and cmp (entry2, entry3) == 0, + then cmp (entry1, entry3) < 0. + - if cmp (entry1, entry2) == 0 and cmp (entry2, entry3) < 0, + then cmp (entry1, entry3) < 0. + - if cmp (entry1, entry2) == 0 and cmp (entry2, entry3) == 0, + then cmp (entry1, entry3) == 0. */ static int hol_entry_cmp (const struct hol_entry *entry1, const struct hol_entry *entry2) { - /* The group numbers by which the entries should be ordered; if either is - in a cluster, then this is just the group within the cluster. */ - int group1 = entry1->group, group2 = entry2->group; - - if (entry1->cluster != entry2->cluster) + /* First, compare the group numbers. For entries within a cluster, what + matters is the group number of the base cluster in which the entry + resides. */ + int group1 = (entry1->cluster + ? hol_cluster_base (entry1->cluster)->group + : entry1->group); + int group2 = (entry2->cluster + ? hol_cluster_base (entry2->cluster)->group + : entry2->group); + int cmp = group_cmp (group1, group2); + if (cmp != 0) + return cmp; + + /* The group numbers are the same. */ + + /* Entries that are not in a cluster come before entries in a cluster. */ + cmp = (entry1->cluster != NULL) - (entry2->cluster != NULL); + if (cmp != 0) + return cmp; + + /* Compare the clusters. */ + if (entry1->cluster != NULL) { - /* The entries are not within the same cluster, so we can't compare them - directly, we have to use the appropriate clustering level too. */ - if (! entry1->cluster) - /* ENTRY1 is at the `base level', not in a cluster, so we have to - compare it's group number with that of the base cluster in which - ENTRY2 resides. Note that if they're in the same group, the - clustered option always comes last. */ - return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); - else if (! entry2->cluster) - /* Likewise, but ENTRY2's not in a cluster. */ - return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); - else - /* Both entries are in clusters, we can just compare the clusters. */ - return hol_cluster_cmp (entry1->cluster, entry2->cluster); + cmp = hol_cluster_cmp (entry1->cluster, entry2->cluster); + if (cmp != 0) + return cmp; } - else if (group1 == group2) - /* The entries are both in the same cluster and group, so compare them - alphabetically. */ + + /* For entries in the same cluster, compare also the group numbers + within the cluster. */ + cmp = group_cmp (entry1->group, entry2->group); + if (cmp != 0) + return cmp; + + /* The entries are both in the same group and the same cluster. */ + + /* 'documentation' options always follow normal options (or documentation + options that *look* like normal options). */ + const char *long1 = hol_entry_first_long (entry1); + const char *long2 = hol_entry_first_long (entry2); + int doc1 = + (odoc (entry1->opt) ? long1 != NULL && canon_doc_option (&long1) : 0); + int doc2 = + (odoc (entry2->opt) ? long2 != NULL && canon_doc_option (&long2) : 0); + cmp = doc1 - doc2; + if (cmp != 0) + return cmp; + + /* Compare the entries alphabetically. */ + + /* First, compare the first character of the options. + Put entries without *any* valid options (such as options with + OPTION_HIDDEN set) first. But as they're not displayed, it doesn't + matter where they are. */ + int short1 = hol_entry_first_short (entry1); + int short2 = hol_entry_first_short (entry2); + unsigned char first1 = short1 ? short1 : long1 != NULL ? *long1 : 0; + unsigned char first2 = short2 ? short2 : long2 != NULL ? *long2 : 0; + /* Compare ignoring case. */ + /* Use tolower, not _tolower, since the latter has undefined behaviour + for characters that are not uppercase letters. */ + cmp = tolower (first1) - tolower (first2); + if (cmp != 0) + return cmp; + /* When the options start with the same letter (ignoring case), lower-case + comes first. */ + cmp = first2 - first1; + if (cmp != 0) + return cmp; + + /* The first character of the options agree. */ + + /* Put entries with a short option before entries without a short option. */ + cmp = (short1 != 0) - (short2 != 0); + if (cmp != 0) + return cmp; + + /* Compare entries without a short option by comparing the long option. */ + if (short1 == 0) { - int short1 = hol_entry_first_short (entry1); - int short2 = hol_entry_first_short (entry2); - int doc1 = odoc (entry1->opt); - int doc2 = odoc (entry2->opt); - const char *long1 = hol_entry_first_long (entry1); - const char *long2 = hol_entry_first_long (entry2); - - if (doc1) - doc1 = long1 != NULL && canon_doc_option (&long1); - if (doc2) - doc2 = long2 != NULL && canon_doc_option (&long2); - - if (doc1 != doc2) - /* `documentation' options always follow normal options (or - documentation options that *look* like normal options). */ - return doc1 - doc2; - else if (!short1 && !short2 && long1 && long2) - /* Only long options. */ - return __strcasecmp (long1, long2); - else - /* Compare short/short, long/short, short/long, using the first - character of long options. Entries without *any* valid - options (such as options with OPTION_HIDDEN set) will be put - first, but as they're not displayed, it doesn't matter where - they are. */ + cmp = (long1 != NULL) - (long2 != NULL); + if (cmp != 0) + return cmp; + + if (long1 != NULL) { - unsigned char first1 = short1 ? short1 : long1 ? *long1 : 0; - unsigned char first2 = short2 ? short2 : long2 ? *long2 : 0; - /* Use tolower, not _tolower, since the latter has undefined - behaviour for characters that are not uppercase letters. */ - int lower_cmp = tolower (first1) - tolower (first2); - /* Compare ignoring case, except when the options are both the - same letter, in which case lower-case always comes first. */ - return lower_cmp ? lower_cmp : first2 - first1; - } + cmp = __strcasecmp (long1, long2); + if (cmp != 0) + return cmp; + } } - else - /* Within the same cluster, but not the same group, so just compare - groups. */ - return group_cmp (group1, group2, 0); + + /* We're out of comparison criteria. At this point, if ENTRY1 != ENTRY2, + the order of these entries will be unpredictable. */ + return 0; } /* Variant of hol_entry_cmp with correct signature for qsort. */