1/*	$NetBSD: stack.c,v 1.8 2020/05/25 20:47:35 christos Exp $	*/
2
3
4/**
5 * \file stack.c
6 *
7 *  This is a special option processing routine that will save the
8 *  argument to an option in a FIFO queue.
9 *
10 * @addtogroup autoopts
11 * @{
12 */
13/*
14 *  This file is part of AutoOpts, a companion to AutoGen.
15 *  AutoOpts is free software.
16 *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
17 *
18 *  AutoOpts is available under any one of two licenses.  The license
19 *  in use must be one of these two and the choice is under the control
20 *  of the user of the license.
21 *
22 *   The GNU Lesser General Public License, version 3 or later
23 *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
24 *
25 *   The Modified Berkeley Software Distribution License
26 *      See the file "COPYING.mbsd"
27 *
28 *  These files have the following sha256 sums:
29 *
30 *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
31 *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
32 *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
33 */
34
35#ifdef WITH_LIBREGEX
36#  include REGEX_HEADER
37#endif
38
39/*=export_func  optionUnstackArg
40 * private:
41 *
42 * what:  Remove option args from a stack
43 * arg:   + tOptions * + opts + program options descriptor +
44 * arg:   + tOptDesc * + od   + the descriptor for this arg +
45 *
46 * doc:
47 *  Invoked for options that are equivalenced to stacked options.
48=*/
49void
50optionUnstackArg(tOptions * opts, tOptDesc * od)
51{
52    tArgList * arg_list;
53
54    if (INQUERY_CALL(opts, od))
55        return;
56
57    arg_list = (tArgList *)od->optCookie;
58
59    /*
60     *  IF we don't have any stacked options,
61     *  THEN indicate that we don't have any of these options
62     */
63    if (arg_list == NULL) {
64        od->fOptState &= OPTST_PERSISTENT_MASK;
65        if ((od->fOptState & OPTST_INITENABLED) == 0)
66            od->fOptState |= OPTST_DISABLED;
67        return;
68    }
69
70#ifdef WITH_LIBREGEX
71    {
72        regex_t   re;
73        int       i, ct, dIdx;
74
75        if (regcomp(&re, od->optArg.argString, REG_NOSUB) != 0)
76            return;
77
78        /*
79         *  search the list for the entry(s) to remove.  Entries that
80         *  are removed are *not* copied into the result.  The source
81         *  index is incremented every time.  The destination only when
82         *  we are keeping a define.
83         */
84        for (i = 0, dIdx = 0, ct = arg_list->useCt; --ct >= 0; i++) {
85            char const * pzSrc = arg_list->apzArgs[ i ];
86            char *       pzEq  = strchr(pzSrc, '=');
87            int          res;
88
89
90            if (pzEq != NULL)
91                *pzEq = NUL;
92
93            res = regexec(&re, pzSrc, (size_t)0, NULL, 0);
94            switch (res) {
95            case 0:
96                /*
97                 *  Remove this entry by reducing the in-use count
98                 *  and *not* putting the string pointer back into
99                 *  the list.
100                 */
101                AGFREE(pzSrc);
102                arg_list->useCt--;
103                break;
104
105            default:
106            case REG_NOMATCH:
107                if (pzEq != NULL)
108                    *pzEq = '=';
109
110                /*
111                 *  IF we have dropped an entry
112                 *  THEN we have to move the current one.
113                 */
114                if (dIdx != i)
115                    arg_list->apzArgs[ dIdx ] = pzSrc;
116                dIdx++;
117            }
118        }
119
120        regfree(&re);
121    }
122#else  /* not WITH_LIBREGEX */
123    {
124        int i, ct, dIdx;
125
126        /*
127         *  search the list for the entry(s) to remove.  Entries that
128         *  are removed are *not* copied into the result.  The source
129         *  index is incremented every time.  The destination only when
130         *  we are keeping a define.
131         */
132        for (i = 0, dIdx = 0, ct = arg_list->useCt; --ct >= 0; i++) {
133            const char * pzSrc = arg_list->apzArgs[ i ];
134            char *       pzEq  = strchr(pzSrc, '=');
135
136            if (pzEq != NULL)
137                *pzEq = NUL;
138
139            if (strcmp(pzSrc, od->optArg.argString) == 0) {
140                /*
141                 *  Remove this entry by reducing the in-use count
142                 *  and *not* putting the string pointer back into
143                 *  the list.
144                 */
145                AGFREE(pzSrc);
146                arg_list->useCt--;
147            } else {
148                if (pzEq != NULL)
149                    *pzEq = '=';
150
151                /*
152                 *  IF we have dropped an entry
153                 *  THEN we have to move the current one.
154                 */
155                if (dIdx != i)
156                    arg_list->apzArgs[ dIdx ] = pzSrc;
157                dIdx++;
158            }
159        }
160    }
161#endif /* WITH_LIBREGEX */
162    /*
163     *  IF we have unstacked everything,
164     *  THEN indicate that we don't have any of these options
165     */
166    if (arg_list->useCt == 0) {
167        od->fOptState &= OPTST_PERSISTENT_MASK;
168        if ((od->fOptState & OPTST_INITENABLED) == 0)
169            od->fOptState |= OPTST_DISABLED;
170        AGFREE(arg_list);
171        od->optCookie = NULL;
172    }
173}
174
175
176/*
177 *  Put an entry into an argument list.  The first argument points to
178 *  a pointer to the argument list structure.  It gets passed around
179 *  as an opaque address.
180 */
181LOCAL void
182addArgListEntry(void ** ppAL, void * entry)
183{
184    tArgList * pAL = *(void **)ppAL;
185
186    /*
187     *  IF we have never allocated one of these,
188     *  THEN allocate one now
189     */
190    if (pAL == NULL) {
191        pAL = (tArgList *)AGALOC(sizeof(*pAL), "new option arg stack");
192        if (pAL == NULL)
193            return;
194        pAL->useCt   = 0;
195        pAL->allocCt = MIN_ARG_ALLOC_CT;
196        *ppAL = VOIDP(pAL);
197    }
198
199    /*
200     *  ELSE if we are out of room
201     *  THEN make it bigger
202     */
203    else if (pAL->useCt >= pAL->allocCt) {
204        size_t sz = sizeof(*pAL);
205        pAL->allocCt += INCR_ARG_ALLOC_CT;
206
207        /*
208         *  The base structure contains space for MIN_ARG_ALLOC_CT
209         *  pointers.  We subtract it off to find our augment size.
210         */
211        sz += sizeof(char *) * ((size_t)pAL->allocCt - MIN_ARG_ALLOC_CT);
212        pAL = (tArgList *)AGREALOC(VOIDP(pAL), sz, "expanded opt arg stack");
213        if (pAL == NULL)
214            return;
215        *ppAL = VOIDP(pAL);
216    }
217
218    /*
219     *  Insert the new argument into the list
220     */
221    pAL->apzArgs[ (pAL->useCt)++ ] = entry;
222}
223
224
225/*=export_func  optionStackArg
226 * private:
227 *
228 * what:  put option args on a stack
229 * arg:   + tOptions * + opts + program options descriptor +
230 * arg:   + tOptDesc * + od   + the descriptor for this arg +
231 *
232 * doc:
233 *  Keep an entry-ordered list of option arguments.
234=*/
235void
236optionStackArg(tOptions * opts, tOptDesc * od)
237{
238    char * pz;
239
240    if (INQUERY_CALL(opts, od))
241        return;
242
243    if ((od->fOptState & OPTST_RESET) != 0) {
244        tArgList * arg_list = od->optCookie;
245        int ix;
246        if (arg_list == NULL)
247            return;
248
249        ix = arg_list->useCt;
250        while (--ix >= 0)
251            AGFREE(arg_list->apzArgs[ix]);
252        AGFREE(arg_list);
253
254    } else {
255        if (od->optArg.argString == NULL)
256            return;
257
258        AGDUPSTR(pz, od->optArg.argString, "stack arg");
259        addArgListEntry(&(od->optCookie), VOIDP(pz));
260    }
261}
262/** @}
263 *
264 * Local Variables:
265 * mode: C
266 * c-file-style: "stroustrup"
267 * indent-tabs-mode: nil
268 * End:
269 * end of autoopts/stack.c */
270