1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-07 12:02:30 +03:00
Files
postgres/src/port/getopt_long.c
Tom Lane 8f1791c618 Fix some cases of indirectly casting away const.
Newest versions of gcc are able to detect cases where code implicitly
casts away const by assigning the result of strchr() or a similar
function applied to a "const char *" value to a target variable
that's just "char *".  This of course creates a hazard of not getting
a compiler warning about scribbling on a string one was not supposed
to, so fixing up such cases is good.

This patch fixes a dozen or so places where we were doing that.
Most are trivial additions of "const" to the target variable,
since no actually-hazardous change was occurring.  There is one
place in ecpg.trailer where we were indeed violating the intention
of not modifying a string passed in as "const char *".  I believe
that's harmless not a live bug, but let's fix it by copying the
string before modifying it.

There is a remaining trouble spot in ecpg/preproc/variable.c,
which requires more complex surgery.  I've left that out of this
commit because I want to study that code a bit more first.

We probably will want to back-patch this once compilers that detect
this pattern get into wider circulation, but for now I'm just
going to apply it to master to see what the buildfarm says.

Thanks to Bertrand Drouvot for finding a couple more spots than
I had.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Discussion: https://postgr.es/m/1324889.1764886170@sss.pgh.pa.us
2025-12-05 11:17:23 -05:00

242 lines
6.1 KiB
C

/*
* getopt_long() -- long options parser
*
* Portions Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Portions Copyright (c) 2003
* PostgreSQL Global Development Group
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* src/port/getopt_long.c
*/
#include "c.h"
#include "getopt_long.h"
#define BADCH '?'
#define BADARG ':'
#define EMSG ""
/*
* getopt_long
* Parse argc/argv argument vector, with long options.
*
* This implementation does not use optreset. Instead, we guarantee that
* it can be restarted on a new argv array after a previous call returned -1,
* if the caller resets optind to 1 before the first call of the new series.
* (Internally, this means we must be sure to reset "place" to EMSG,
* "nonopt_start" to -1, and "force_nonopt" to false before returning -1.)
*
* Note that this routine reorders the pointers in argv (despite the const
* qualifier) so that all non-options will be at the end when -1 is returned.
*/
int
getopt_long(int argc, char *const argv[],
const char *optstring,
const struct option *longopts, int *longindex)
{
static char *place = EMSG; /* option letter processing */
const char *oli; /* option letter list index */
static int nonopt_start = -1;
static bool force_nonopt = false;
if (!*place)
{ /* update scanning pointer */
char **args = (char **) argv;
retry:
/*
* If we are out of arguments or only non-options remain, return -1.
*/
if (optind >= argc || optind == nonopt_start)
{
place = EMSG;
nonopt_start = -1;
force_nonopt = false;
return -1;
}
place = argv[optind];
/*
* An argument is a non-option if it meets any of the following
* criteria: it follows an argument that is equivalent to the string
* "--", it does not start with '-', or it is equivalent to the string
* "-". When we encounter a non-option, we move it to the end of argv
* (after shifting all remaining arguments over to make room), and
* then we try again with the next argument.
*/
if (force_nonopt || place[0] != '-' || place[1] == '\0')
{
for (int i = optind; i < argc - 1; i++)
args[i] = args[i + 1];
args[argc - 1] = place;
if (nonopt_start == -1)
nonopt_start = argc - 1;
else
nonopt_start--;
goto retry;
}
place++;
if (place[0] == '-' && place[1] == '\0')
{
/* found "--", treat it as end of options */
++optind;
force_nonopt = true;
goto retry;
}
if (place[0] == '-' && place[1])
{
/* long option */
size_t namelen;
int i;
place++;
namelen = strcspn(place, "=");
for (i = 0; longopts[i].name != NULL; i++)
{
if (strlen(longopts[i].name) == namelen
&& strncmp(place, longopts[i].name, namelen) == 0)
{
int has_arg = longopts[i].has_arg;
if (has_arg != no_argument)
{
if (place[namelen] == '=')
optarg = place + namelen + 1;
else if (optind < argc - 1 &&
has_arg == required_argument)
{
optind++;
optarg = argv[optind];
}
else
{
if (optstring[0] == ':')
return BADARG;
if (opterr && has_arg == required_argument)
fprintf(stderr,
"%s: option requires an argument -- %s\n",
argv[0], place);
place = EMSG;
optind++;
if (has_arg == required_argument)
return BADCH;
optarg = NULL;
}
}
else
{
optarg = NULL;
if (place[namelen] != 0)
{
/* XXX error? */
}
}
optind++;
if (longindex)
*longindex = i;
place = EMSG;
if (longopts[i].flag == NULL)
return longopts[i].val;
else
{
*longopts[i].flag = longopts[i].val;
return 0;
}
}
}
if (opterr && optstring[0] != ':')
fprintf(stderr,
"%s: illegal option -- %s\n", argv[0], place);
place = EMSG;
optind++;
return BADCH;
}
}
/* short option */
optopt = (int) *place++;
oli = strchr(optstring, optopt);
if (!oli)
{
if (!*place)
++optind;
if (opterr && *optstring != ':')
fprintf(stderr,
"%s: illegal option -- %c\n", argv[0], optopt);
return BADCH;
}
if (oli[1] != ':')
{ /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else
{ /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (argc <= ++optind)
{ /* no arg */
place = EMSG;
if (*optstring == ':')
return BADARG;
if (opterr)
fprintf(stderr,
"%s: option requires an argument -- %c\n",
argv[0], optopt);
return BADCH;
}
else
/* white space */
optarg = argv[optind];
place = EMSG;
++optind;
}
return optopt;
}