workaround clang-14 bug
[LibreOffice.git] / soltools / cpp / _macro.c
blobfbf496b8f78124665f0764d3f9863ab30986a838
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #if defined(__IBMC__) || defined(__EMX__) || defined(_MSC_VER)
24 # ifndef PATH_MAX
25 # define PATH_MAX _MAX_PATH
26 # endif
27 #endif
28 #include <limits.h>
30 #include <sal/types.h>
32 #include "cpp.h"
34 #define NCONCAT 16384
37 * do a macro definition. tp points to the name being defined in the line
39 void
40 dodefine(Tokenrow * trp)
42 Token *tp;
43 Nlist *np;
44 Source *s;
45 Tokenrow *def, *args;
46 static uchar location[(PATH_MAX + 8) * NINC], *cp;
48 tp = trp->tp + 1;
49 if (tp >= trp->lp || tp->type != NAME)
51 error(ERROR, "#defined token is not a name");
52 return;
54 np = lookup(tp, 1);
55 if (np->flag & ISUNCHANGE)
57 error(ERROR, "#defined token %t can't be redefined", tp);
58 return;
60 /* collect arguments */
61 tp += 1;
62 args = NULL;
63 if (tp < trp->lp && tp->type == LP && tp->wslen == 0)
65 tp += 1;
66 args = new(Tokenrow);
67 maketokenrow(2, args);
68 if (tp->type != RP)
70 /* macro with args */
71 size_t narg = 0;
72 int err = 0;
74 for (;;)
76 Token *atp;
78 if (tp->type != NAME)
80 err++;
81 break;
83 if (narg >= args->max)
84 growtokenrow(args);
85 for (atp = args->bp; atp < args->lp; atp++)
86 if (atp->len == tp->len
87 && strncmp((char *) atp->t, (char *) tp->t, tp->len) == 0)
88 error(ERROR, "Duplicate macro argument");
89 *args->lp++ = *tp;
90 narg++;
91 tp += 1;
92 if (tp->type == RP)
93 break;
94 if (tp->type != COMMA)
96 err++;
97 break;
99 tp += 1;
101 if (err)
103 error(ERROR, "Syntax error in macro parameters");
104 return;
107 tp += 1;
109 trp->tp = tp;
110 if (((trp->lp) - 1)->type == NL)
111 trp->lp -= 1;
112 def = normtokenrow(trp);
113 if (np->flag & ISDEFINED)
115 if (comparetokens(def, np->vp)
116 || (np->ap == NULL) != (args == NULL)
117 || (np->ap && comparetokens(args, np->ap)))
119 if ( np->loc )
120 error(ERROR,
121 "Macro redefinition of %t (already defined at %s)",
122 trp->bp + 2, np->loc);
123 else
124 error(ERROR,
125 "Macro redefinition of %t (already defined at %s)",
126 trp->bp + 2, "commandline" );
129 if (args)
131 Tokenrow *tap;
133 tap = normtokenrow(args);
134 dofree(args->bp);
135 dofree(args);
136 args = tap;
138 np->ap = args;
139 np->vp = def;
140 np->flag |= ISDEFINED;
142 /* build location string of macro definition */
143 for (cp = location, s = cursource; s; s = s->next)
144 if (*s->filename)
146 if (cp != location)
147 *cp++ = ' ';
148 SAL_WNODEPRECATED_DECLARATIONS_PUSH /* sprintf (macOS 13 SDK) */
149 sprintf((char *)cp, "%s:%d", s->filename, s->line);
150 SAL_WNODEPRECATED_DECLARATIONS_POP
151 cp += strlen((char *)cp);
154 np->loc = newstring(location, strlen((char *)location), 0);
156 if (Mflag)
158 if (np->ap)
159 error(INFO, "Macro definition of %s(%r) [%r]", np->name, np->ap, np->vp);
160 else
161 error(INFO, "Macro definition of %s [%r]", np->name, np->vp);
166 * Definition received via -D or -U
168 void
169 doadefine(Tokenrow * trp, int type)
171 Nlist *np;
172 static uchar onestr[2] = "1";
173 static Token onetoken[1] = {{NUMBER, 0, 1, onestr, 0}};
174 static Tokenrow onetr = {onetoken, onetoken, onetoken + 1, 1};
176 trp->tp = trp->bp;
177 if (type == 'U')
179 if (trp->lp - trp->tp != 2 || trp->tp->type != NAME)
180 goto syntax;
181 if ((np = lookup(trp->tp, 0)) == NULL)
182 return;
183 np->flag &= ~ISDEFINED;
184 return;
187 if (type == 'A')
189 if (trp->tp >= trp->lp || trp->tp->type != NAME)
190 goto syntax;
191 trp->tp->type = ARCHITECTURE;
192 np = lookup(trp->tp, 1);
193 np->flag |= ISARCHITECTURE;
194 trp->tp += 1;
195 if (trp->tp >= trp->lp || trp->tp->type == END)
197 np->vp = &onetr;
198 return;
200 else
201 error(FATAL, "Illegal -A argument %r", trp);
204 if (trp->tp >= trp->lp || trp->tp->type != NAME)
205 goto syntax;
206 np = lookup(trp->tp, 1);
207 np->flag |= ISDEFINED;
208 trp->tp += 1;
209 if (trp->tp >= trp->lp || trp->tp->type == END)
211 np->vp = &onetr;
212 return;
214 if (trp->tp->type != ASGN)
215 goto syntax;
216 trp->tp += 1;
217 if ((trp->lp - 1)->type == END)
218 trp->lp -= 1;
219 np->vp = normtokenrow(trp);
220 return;
221 syntax:
222 error(FATAL, "Illegal -D or -U argument %r", trp);
228 * Do macro expansion in a row of tokens.
229 * Flag is NULL if more input can be gathered.
231 void
232 expandrow(Tokenrow * trp, char *flag)
234 Token * tp;
235 Nlist * np;
237 MacroValidatorList validators;
238 mvl_init(&validators);
239 /* Sets all token-identifiers to 0 because tokens may not be initialised (never use C!) */
240 tokenrow_zeroTokenIdentifiers(trp);
242 if (flag)
243 setsource(flag, -1, -1, "", 0);
244 for (tp = trp->tp; tp < trp->lp;)
246 mvl_check(&validators, tp);
248 if (tp->type != NAME
249 || quicklook(tp->t[0], tp->len > 1 ? tp->t[1] : 0) == 0
250 || (np = lookup(tp, 0)) == NULL
251 || (np->flag & (ISDEFINED | ISMAC)) == 0
252 || (np->flag & ISACTIVE) != 0)
254 tp++;
255 continue;
257 trp->tp = tp;
258 if (np->val == KDEFINED)
260 tp->type = DEFINED;
261 if ((tp + 1) < trp->lp && (tp + 1)->type == NAME)
262 (tp + 1)->type = NAME1;
263 else
264 if ((tp + 3) < trp->lp && (tp + 1)->type == LP
265 && (tp + 2)->type == NAME && (tp + 3)->type == RP)
266 (tp + 2)->type = NAME1;
267 else
268 error(ERROR, "Incorrect syntax for `defined'");
269 tp++;
270 continue;
272 else
273 if (np->val == KMACHINE)
275 if (((tp - 1) >= trp->bp) && ((tp - 1)->type == SHARP))
277 tp->type = ARCHITECTURE;
278 if ((tp + 1) < trp->lp && (tp + 1)->type == NAME)
279 (tp + 1)->type = NAME2;
280 else
281 if ((tp + 3) < trp->lp && (tp + 1)->type == LP
282 && (tp + 2)->type == NAME && (tp + 3)->type == RP)
283 (tp + 2)->type = NAME2;
284 else
285 error(ERROR, "Incorrect syntax for `#machine'");
287 tp++;
288 continue;
291 if (np->flag & ISMAC)
292 builtin(trp, np->val);
293 else
295 // coverity[overrun-buffer-arg: FALSE] - a multiple of trp->max is allocated, not trp->max itself
296 expand(trp, np, &validators);
298 tp = trp->tp;
299 } // end for
300 if (flag)
301 unsetsource();
303 mvl_destruct(&validators);
307 * Expand the macro whose name is np, at token trp->tp, in the tokenrow.
308 * Return trp->tp at the first token next to be expanded
309 * (ordinarily the beginning of the expansion)
310 * I.e.: the same position as before!
311 * Only one expansion is performed, then we return to the expandrow()
312 * loop and start at same position.
314 void
315 expand(Tokenrow * trp, Nlist * np, MacroValidatorList * pValidators)
317 Tokenrow ntr;
318 int ntokc, narg;
319 Tokenrow *atr[NARG + 1];
321 if (Mflag == 2)
323 if (np->ap)
324 error(INFO, "Macro expansion of %t with %s(%r)", trp->tp, np->name, np->ap);
325 else
326 error(INFO, "Macro expansion of %t with %s", trp->tp, np->name);
329 copytokenrow(&ntr, np->vp); /* copy macro value */
330 if (np->ap == NULL) /* parameterless */
331 ntokc = 1;
332 else
334 int i;
336 ntokc = gatherargs(trp, atr, &narg);
337 if (narg < 0)
338 { /* not actually a call (no '(') */
339 trp->tp++;
340 return;
342 if (narg != rowlen(np->ap))
344 error(ERROR, "Disagreement in number of macro arguments");
345 trp->tp += ntokc;
346 return;
349 /** If gatherargs passed a macro validating token, this token
350 must become valid here.
351 trp->tp+0 was checked in expandrow(), so we don't need to do it
352 again here:
354 for (i = 1; i < ntokc; i++)
356 mvl_check(pValidators,trp->tp+i);
359 substargs(np, &ntr, atr); /* put args into replacement */
360 for (i = 0; i < narg; i++)
362 dofree(atr[i]->bp);
363 dofree(atr[i]);
367 doconcat(&ntr); /* execute ## operators */
368 ntr.tp = ntr.bp;
369 makespace(&ntr, trp->tp);
371 tokenrow_zeroTokenIdentifiers(&ntr);
372 insertrow(trp, ntokc, &ntr);
374 /* add validator for just invalidated macro:
376 np->flag |= ISACTIVE;
377 if (trp->tp != trp->lp)
378 { /* tp is a valid pointer: */
379 mvl_add(pValidators,np,trp->tp);
381 else
382 { /* tp is == lp, therefore does not point to valid memory: */
383 mvl_add(pValidators,np,NULL);
385 /* reset trp->tp to original position:
387 trp->tp -= ntr.lp - ntr.bp; /* so the result will be tested for macros from the same position again */
389 dofree(ntr.bp);
391 return;
395 * Gather an arglist, starting in trp with tp pointing at the macro name.
396 * Return total number of tokens passed, stash number of args found.
397 * trp->tp is not changed relative to the tokenrow.
400 gatherargs(Tokenrow * trp, Tokenrow ** atr, int *narg)
402 int parens = 1;
403 int ntok = 0;
404 Token *bp, *lp;
405 Tokenrow ttr;
406 int ntokp;
407 int needspace;
409 *narg = -1; /* means that there is no macro
410 * call */
411 /* look for the ( */
412 for (;;)
414 trp->tp++;
415 ntok++;
416 if (trp->tp >= trp->lp)
418 // coverity[overrun-buffer-arg: FALSE] - a multiple of trp->max is allocated, not trp->max itself
419 gettokens(trp, 0);
420 if ((trp->lp - 1)->type == END)
422 trp->lp -= 1;
423 trp->tp -= ntok;
424 return ntok;
427 if (trp->tp->type == LP)
428 break;
429 if (trp->tp->type != NL)
430 return ntok;
432 *narg = 0;
433 ntok++;
434 ntokp = ntok;
435 trp->tp++;
436 /* search for the terminating ), possibly extending the row */
437 needspace = 0;
438 while (parens > 0)
440 if (trp->tp >= trp->lp)
442 // coverity[overrun-buffer-arg: FALSE] - a multiple of trp->max is allocated, not trp->max itself
443 gettokens(trp, 0);
445 if (needspace)
447 needspace = 0;
448 /* makespace(trp); [rh] */
450 if (trp->tp->type == END)
452 trp->lp -= 1;
453 trp->tp -= ntok;
454 error(ERROR, "EOF in macro arglist");
455 return ntok;
457 if (trp->tp->type == NL)
459 trp->tp += 1;
460 adjustrow(trp, -1);
461 trp->tp -= 1;
462 /* makespace(trp); [rh] */
463 needspace = 1;
464 continue;
466 if (trp->tp->type == LP)
467 parens++;
468 else
469 if (trp->tp->type == RP)
470 parens--;
471 trp->tp++;
472 ntok++;
474 trp->tp -= ntok;
475 /* Now trp->tp won't move underneath us */
476 lp = bp = trp->tp + ntokp;
477 for (; parens >= 0; lp++)
479 if (lp->type == LP)
481 parens++;
482 continue;
484 if (lp->type == RP)
485 parens--;
486 if (lp->type == DSHARP)
487 lp->type = DSHARP1; /* ## not special in arg */
488 if ((lp->type == COMMA && parens == 0) ||
489 ( parens < 0 && ((lp - 1)->type != LP)))
491 if (*narg >= NARG - 1)
492 error(FATAL, "Sorry, too many macro arguments");
493 ttr.bp = ttr.tp = bp;
494 ttr.lp = lp;
495 atr[(*narg)++] = normtokenrow(&ttr);
496 bp = lp + 1;
499 return ntok;
503 * substitute the argument list into the replacement string
504 * This would be simple except for ## and #
506 void
507 substargs(Nlist * np, Tokenrow * rtr, Tokenrow ** atr)
509 Tokenrow tatr;
510 Token *tp;
511 int ntok, argno;
513 for (rtr->tp = rtr->bp; rtr->tp < rtr->lp;)
515 if (rtr->tp->type == SHARP)
516 { /* string operator */
517 tp = rtr->tp;
518 rtr->tp += 1;
519 if ((argno = lookuparg(np, rtr->tp)) < 0)
521 error(ERROR, "# not followed by macro parameter");
522 continue;
524 ntok = 1 + (int)(rtr->tp - tp);
525 rtr->tp = tp;
526 insertrow(rtr, ntok, stringify(atr[argno]));
527 continue;
529 if (rtr->tp->type == NAME
530 && (argno = lookuparg(np, rtr->tp)) >= 0)
532 if (((rtr->tp + 1) < rtr->lp && (rtr->tp + 1)->type == DSHARP)
533 || (rtr->tp != rtr->bp && (rtr->tp - 1)->type == DSHARP))
535 copytokenrow(&tatr, atr[argno]);
536 makespace(&tatr, rtr->tp);
537 insertrow(rtr, 1, &tatr);
538 dofree(tatr.bp);
540 else
542 copytokenrow(&tatr, atr[argno]);
543 makespace(&tatr, rtr->tp);
544 expandrow(&tatr, "<macro>");
545 insertrow(rtr, 1, &tatr);
546 dofree(tatr.bp);
548 continue;
550 rtr->tp++;
555 * Evaluate the ## operators in a tokenrow
557 void
558 doconcat(Tokenrow * trp)
560 Token *ltp, *ntp;
561 Tokenrow ntr;
562 size_t len;
564 for (trp->tp = trp->bp; trp->tp < trp->lp; trp->tp++)
566 if (trp->tp->type == DSHARP1)
567 trp->tp->type = DSHARP;
568 else
569 if (trp->tp->type == DSHARP)
571 int i;
572 char tt[NCONCAT];
574 ltp = trp->tp - 1;
575 ntp = trp->tp + 1;
577 if (ltp < trp->bp || ntp >= trp->lp)
579 error(ERROR, "## occurs at border of replacement");
580 continue;
583 ntp = ltp;
584 i = 1;
585 len = 0;
589 if (len + ntp->len + ntp->wslen > sizeof(tt))
591 error(ERROR, "## string concatenation buffer overrun");
592 break;
595 if (ntp != trp->tp + 1)
597 strncpy((char *) tt + len, (char *) ntp->t - ntp->wslen,
598 ntp->len + ntp->wslen);
599 len += ntp->len + ntp->wslen;
601 else
603 // remove spaces around ##
604 strncpy((char *) tt + len, (char *) ntp->t, ntp->len);
605 len += ntp->len;
608 ntp = trp->tp + i;
609 i++;
611 while (ntp < trp->lp);
613 tt[len] = '\0';
614 setsource("<##>", -1, -1, tt, 0);
615 maketokenrow(3, &ntr);
616 // coverity[overrun-buffer-arg: FALSE] - a multiple of trp->max is allocated, not trp->max itself
617 gettokens(&ntr, 1);
618 unsetsource();
619 if (ntr.bp->type == UNCLASS)
620 error(WARNING, "Bad token %r produced by ##", &ntr);
621 while ((ntr.lp-1)->len == 0 && ntr.lp != ntr.bp)
622 ntr.lp--;
624 doconcat(&ntr);
625 trp->tp = ltp;
626 makespace(&ntr, ltp);
627 insertrow(trp, (int)(ntp - ltp), &ntr);
628 dofree(ntr.bp);
629 trp->tp--;
635 * tp is a potential parameter name of macro mac;
636 * look it up in mac's arglist, and if found, return the
637 * corresponding index in the argname array. Return -1 if not found.
640 lookuparg(Nlist * mac, Token const * tp)
642 Token *ap;
644 if (tp->type != NAME || mac->ap == NULL)
645 return -1;
646 for (ap = mac->ap->bp; ap < mac->ap->lp; ap++)
648 if (ap->len == tp->len && strncmp((char *) ap->t, (char *) tp->t, ap->len) == 0)
649 return (int)(ap - mac->ap->bp);
651 return -1;
655 * Return a quoted version of the tokenrow (from # arg)
657 #define STRLEN 512
658 Tokenrow *
659 stringify(Tokenrow * vp)
661 static Token t = {STRING, 0, 0, NULL, 0};
662 static Tokenrow tr = {&t, &t, &t + 1, 1};
663 Token *tp;
664 uchar s[STRLEN];
665 uchar *sp = s, *cp;
666 int i, instring;
668 *sp++ = '"';
669 for (tp = vp->bp; tp < vp->lp; tp++)
671 instring = tp->type == STRING || tp->type == CCON;
672 if (sp + 2 * tp->len + tp->wslen >= &s[STRLEN - 10])
674 error(ERROR, "Stringified macro arg is too long");
675 break;
678 // Change by np 31.10.2001, #93725 - begin
679 if ( tp->wslen > 0 )
680 *sp++ = ' ';
681 // change end.
683 for (i = 0, cp = tp->t; (unsigned int)i < tp->len; i++)
685 if (instring && (*cp == '"' || *cp == '\\'))
686 *sp++ = '\\';
687 *sp++ = *cp++;
690 *sp++ = '"';
691 *sp = '\0';
692 sp = s;
693 t.len = strlen((char *) sp);
694 t.t = newstring(sp, t.len, 0);
695 return &tr;
699 * expand a builtin name
701 void
702 builtin(Tokenrow * trp, int biname)
704 char *op;
705 Token *tp;
706 Source *s;
708 tp = trp->tp;
709 trp->tp++;
710 /* need to find the real source */
711 s = cursource;
712 while (s && s->fd == -1)
713 s = s->next;
714 if (s == NULL)
715 s = cursource;
716 /* most are strings */
717 tp->type = STRING;
718 if (tp->wslen)
720 *outptr++ = ' ';
721 tp->wslen = 1;
723 op = outptr;
724 *op++ = '"';
725 switch (biname)
728 case KLINENO:
729 tp->type = NUMBER;
730 op = outnum(op - 1, s->line);
731 break;
733 case KFILE:
735 char *src = s->filename;
737 while ((*op++ = *src++) != 0)
738 if (src[-1] == '\\')
739 *op++ = '\\';
740 op--;
741 break;
744 case KDATE:
745 strncpy(op, curtime + 4, 7);
746 strncpy(op + 7, curtime + 20, 4);
747 op += 11;
748 break;
750 case KTIME:
751 strncpy(op, curtime + 11, 8);
752 op += 8;
753 break;
755 default:
756 error(ERROR, "cpp botch: unknown internal macro");
757 return;
759 if (tp->type == STRING)
760 *op++ = '"';
761 tp->t = (uchar *) outptr;
762 tp->len = op - outptr;
763 outptr = op;
766 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */