Add overflow detection
[openscop.git] / source / util.c
blobc13d8453b9ea56607d334198e0bdcfff5a9bbb9d
2 /*+-----------------------------------------------------------------**
3 ** OpenScop Library **
4 **-----------------------------------------------------------------**
5 ** util.c **
6 **-----------------------------------------------------------------**
7 ** First version: 08/10/2010 **
8 **-----------------------------------------------------------------**
11 *****************************************************************************
12 * OpenScop: Structures and formats for polyhedral tools to talk together *
13 *****************************************************************************
14 * ,___,,_,__,,__,,__,,__,,_,__,,_,__,,__,,___,_,__,,_,__, *
15 * / / / // // // // / / / // // / / // / /|,_, *
16 * / / / // // // // / / / // // / / // / / / /\ *
17 * |~~~|~|~~~|~~~|~~~|~~~|~|~~~|~|~~~|~~~|~~~|~|~~~|~|~~~|/_/ \ *
18 * | G |C| P | = | L | P |=| = |C| = | = | = |=| = |=| C |\ \ /\ *
19 * | R |l| o | = | e | l |=| = |a| = | = | = |=| = |=| L | \# \ /\ *
20 * | A |a| l | = | t | u |=| = |n| = | = | = |=| = |=| o | |\# \ \ *
21 * | P |n| l | = | s | t |=| = |d| = | = | = | | |=| o | | \# \ \ *
22 * | H | | y | | e | o | | = |l| | | = | | | | G | | \ \ \ *
23 * | I | | | | e | | | | | | | | | | | | | \ \ \ *
24 * | T | | | | | | | | | | | | | | | | | \ \ \ *
25 * | E | | | | | | | | | | | | | | | | | \ \ \ *
26 * | * |*| * | * | * | * |*| * |*| * | * | * |*| * |*| * | / \* \ \ *
27 * | O |p| e | n | S | c |o| p |-| L | i | b |r| a |r| y |/ \ \ / *
28 * '---'-'---'---'---'---'-'---'-'---'---'---'-'---'-'---' '--' *
29 * *
30 * Copyright (C) 2008 University Paris-Sud 11 and INRIA *
31 * *
32 * (3-clause BSD license) *
33 * Redistribution and use in source and binary forms, with or without *
34 * modification, are permitted provided that the following conditions *
35 * are met: *
36 * *
37 * 1. Redistributions of source code must retain the above copyright notice, *
38 * this list of conditions and the following disclaimer. *
39 * 2. Redistributions in binary form must reproduce the above copyright *
40 * notice, this list of conditions and the following disclaimer in the *
41 * documentation and/or other materials provided with the distribution. *
42 * 3. The name of the author may not be used to endorse or promote products *
43 * derived from this software without specific prior written permission. *
44 * *
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR *
46 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES *
47 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
48 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, *
49 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT *
50 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
51 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY *
52 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
53 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF *
54 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
55 * *
56 * OpenScop Library, a library to manipulate OpenScop formats and data *
57 * structures. Written by: *
58 * Cedric Bastoul <Cedric.Bastoul@u-psud.fr> and *
59 * Louis-Noel Pouchet <Louis-Noel.pouchet@inria.fr> *
60 * *
61 *****************************************************************************/
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <ctype.h>
66 #include <string.h>
68 #include <osl/macros.h>
69 #include <osl/util.h>
72 /*+***************************************************************************
73 * Utility functions *
74 *****************************************************************************/
77 /**
78 * osl_util_skip_blank_and_comments "file skip" function:
79 * this function reads the open file 'file' line by line and skips
80 * blank/comment lines and spaces. The first line where there is some
81 * useful information is stored at the address 'str' (the memory to
82 * store the line must be allocated before the call to this function
83 * and must be at least OSL_MAX_STRING * sizeof(char)). The pointer
84 * to the first useful information in this line is returned by the
85 * function.
86 * \param[in] file The (opened) file to read.
87 * \param[in] str Address of an allocated space to store the first line
88 * that contains useful information.
89 * \return The address of the first useful digit in str.
91 char * osl_util_skip_blank_and_comments(FILE * file, char * str) {
92 char * start;
94 do {
95 start = fgets(str, OSL_MAX_STRING, file);
96 while ((start != NULL) && isspace(*start) && (*start != '\n'))
97 start++;
99 while (start != NULL && (*start == '#' || *start == '\n'));
101 return start;
106 * osl_util_sskip_blank_and_comments "string skip" function:
107 * this function updates the str pointer, which initialy points to a string,
108 * to the first character in this string which is not a space or a comment
109 * (comments start at '#' and end at '\n'), or to the end of string.
110 * \param[in,out] str Address of a string, updated to the address of
111 * the first non-space or comment character.
113 void osl_util_sskip_blank_and_comments(char ** str) {
114 do {
115 // Skip spaces/blanc lines.
116 while (*str && **str && isspace(**str))
117 (*str)++;
119 // Skip the comment if any.
120 if (*str && **str && **str == '#') {
121 while (**str && **str != '\n') {
122 (*str)++;
126 while (*str && **str && **str == '\n');
131 * osl_util_read_int function:
132 * reads an int on the input 'file' or the input string 'str' depending on
133 * which one is not NULL (exactly one of them must not be NULL).
134 * \param[in] file The file where to read an int (if not NULL).
135 * \param[in,out] str The string where to read an int (if not NULL). This
136 * pointer is updated to reflect the read and points
137 * after the int in the input string.
138 * \return The int that has been read.
140 int osl_util_read_int(FILE * file, char ** str) {
141 char s[OSL_MAX_STRING], * start;
142 int res;
143 int i = 0;
145 if ((file != NULL && str != NULL) || (file == NULL && str == NULL))
146 OSL_error("one and only one of the two parameters can be non-NULL");
148 if (file != NULL) {
149 // Parse from a file.
150 start = osl_util_skip_blank_and_comments(file, s);
151 if (sscanf(start, " %d", &res) != 1)
152 OSL_error("an int was expected");
154 else {
155 // Parse from a string.
156 // Skip blank/commented lines.
157 osl_util_sskip_blank_and_comments(str);
159 // Build the chain to analyze.
160 while (**str && !isspace(**str) && **str != '\n' && **str != '#')
161 s[i++] = *((*str)++);
162 s[i] = '\0';
163 if (sscanf(s, "%d", &res) != 1)
164 OSL_error("an int was expected");
167 return res;
172 * osl_util_read_string function:
173 * reads a string on the input 'file' or the input string 'str' depending on
174 * which one is not NULL (exactly one of them must not be NULL).
175 * \param[in] file The file where to read a string (if not NULL).
176 * \param[in,out] str The string where to read a string (if not NULL). This
177 * pointer is updated to reflect the read and points
178 * after the string in the input string.
179 * \return The string that has been read.
181 char * osl_util_read_string(FILE * file, char ** str) {
182 char s[OSL_MAX_STRING], * start;
183 char * res;
184 int i = 0;
186 if ((file != NULL && str != NULL) || (file == NULL && str == NULL))
187 OSL_error("one and only one of the two parameters can be non-NULL");
189 OSL_malloc(res, char *, OSL_MAX_STRING * sizeof(char));
190 if (file != NULL) {
191 // Parse from a file.
192 start = osl_util_skip_blank_and_comments(file, s);
193 if (sscanf(start, " %s", res) != 1)
194 OSL_error("a string was expected");
196 else {
197 // Parse from a string.
198 // Skip blank/commented lines.
199 osl_util_sskip_blank_and_comments(str);
201 // Build the chain to analyze.
202 while (**str && !isspace(**str) && **str != '\n' && **str != '#')
203 s[i++] = *((*str)++);
204 s[i] = '\0';
205 if (sscanf(s, "%s", res) != 1)
206 OSL_error("a string was expected");
209 OSL_realloc(res, char *, strlen(res) + 1);
210 return res;
215 * osl_util_read_line function:
216 * reads a line on the input 'file' or the input string 'str' depending on
217 * which one is not NULL (exactly one of them must not be NULL). A line
218 * is defined as the array of characters before the comment tag or the end of
219 * line (it may include spaces).
220 * \param[in] file The file where to read a line (if not NULL).
221 * \param[in,out] str The string where to read a line (if not NULL). This
222 * pointer is updated to reflect the read and points
223 * after the line in the input string.
224 * \return The line that has been read.
226 char * osl_util_read_line(FILE * file, char ** str) {
227 char s[OSL_MAX_STRING], * start;
228 char * res;
229 int i = 0;
231 if ((file != NULL && str != NULL) || (file == NULL && str == NULL))
232 OSL_error("one and only one of the two parameters can be non-NULL");
234 OSL_malloc(res, char *, OSL_MAX_STRING * sizeof(char));
235 if (file != NULL) {
236 // Parse from a file.
237 start = osl_util_skip_blank_and_comments(file, s);
238 while (*start && *start != '\n' && *start != '#' && i < OSL_MAX_STRING)
239 res[i++] = *start++;
241 else {
242 // Parse from a string.
243 osl_util_sskip_blank_and_comments(str);
244 while (**str && **str != '\n' && **str != '#' && i < OSL_MAX_STRING)
245 res[i++] = *((*str)++);
248 res[i] = '\0';
249 OSL_realloc(res, char *, strlen(res) + 1);
250 return res;
255 * osl_util_read_int internal function:
256 * reads a tag (the form of a tag with name "name" is \<name\>) on the input
257 * 'file' or the input string 'str' depending on which one is not NULL (exactly
258 * one of them must not be NULL). It returns the name of the tag (thus without
259 * the < and > as a string. Note that in the case of an ending tag, e.g.,
260 * \</foo\>, the slash is returned as a part of the name, e.g., /foo. If no
261 * tag is found the function returns NULL.
262 * \param[in] file The file where to read a tag (if not NULL).
263 * \param[in,out] str The string where to read a tag (if not NULL). This
264 * pointer is updated to reflect the read and points
265 * after the tag in the input string.
266 * \return The tag name that has been read.
268 char * osl_util_read_tag(FILE * file, char ** str) {
269 char s[OSL_MAX_STRING], * start;
270 char * res;
271 int i = 0;
273 if ((file != NULL && str != NULL) || (file == NULL && str == NULL))
274 OSL_error("one and only one of the two parameters can be non-NULL");
276 // Skip blank/commented lines.
277 if (file != NULL) {
278 start = osl_util_skip_blank_and_comments(file, s);
279 str = &start;
281 else {
282 osl_util_sskip_blank_and_comments(str);
285 // If the end of the input has been reached, return NULL.
286 if (((file != NULL) && (feof(file))) ||
287 ((str != NULL) && (**str == '\0')))
288 return NULL;
290 // Pass the starting '<'.
291 if (**str != '<')
292 OSL_error("a \"<\" to start a tag was expected");
293 (*str)++;
295 // Read the tag.
296 OSL_malloc(res, char *, (OSL_MAX_STRING + 1) * sizeof(char));
297 res[OSL_MAX_STRING] = '\0';
299 while (**str && **str != '>') {
300 if (((**str >= 'A') && (**str <= 'Z')) ||
301 ((**str >= 'a') && (**str <= 'z')) ||
302 ((**str == '/') && (i == 0)) ||
303 (**str == '_')) {
304 res[i++] = *((*str)++);
305 res[i] = '\0';
307 else {
308 OSL_error("illegal character in the tag name");
312 // Check we actually end up with a '>' and pass it.
313 if (**str != '>')
314 OSL_error("a \">\" to end a tag was expected");
315 (*str)++;
317 return res;
322 * osl_util_read_uptoflag function:
323 * this function reads a string up to a given flag (the flag is read)
324 * on the input 'file' or the input string 'str' depending on which one is
325 * not NULL (exactly one of them must not be NULL) and returns that string
326 * without the flag. It returns NULL if the flag is not found.
327 * \param[in] file The file where to read up to flag (if not NULL).
328 * \param[in,out] str The string where to read up to flag (if not NULL). This
329 * pointer is updated to reflect the read and points
330 * after the flag in the input string.
331 * \param[in] flag The flag which, when reached, stops the reading.
332 * \return The string that has been read.
334 char * osl_util_read_uptoflag(FILE * file, char ** str, char * flag) {
335 int high_water_mark = OSL_MAX_STRING;
336 int nb_chars = 0;
337 int lenflag = strlen(flag), lenstr;
338 int flag_found = 0;
339 char * res;
341 if ((file != NULL && str != NULL) || (file == NULL && str == NULL))
342 OSL_error("one and only one of the two parameters can be non-NULL");
344 OSL_malloc(res, char *, high_water_mark * sizeof(char));
346 // Copy everything to the res string.
347 if (str != NULL)
348 lenstr = strlen(*str);
349 while (((str != NULL) && (nb_chars != lenstr)) ||
350 ((file != NULL) && (!feof(file)))) {
351 res[nb_chars++] = (str != NULL) ? *((*str)++) : fgetc(file);
353 if ((nb_chars >= lenflag) &&
354 (!strncmp(&res[nb_chars - lenflag], flag, lenflag))) {
355 flag_found = 1;
356 break;
359 if (nb_chars >= high_water_mark) {
360 high_water_mark += high_water_mark;
361 OSL_realloc(res, char *, high_water_mark * sizeof(char));
365 if (!flag_found) {
366 OSL_debug("flag was not found, end of input reached");
367 free(res);
368 return NULL;
371 // - 0-terminate the string.
372 OSL_realloc(res, char *, (nb_chars - strlen(flag) + 1) * sizeof(char));
373 res[nb_chars - strlen(flag)] = '\0';
375 return res;
380 * osl_util_read_uptotag function:
381 * this function reads a string up to a given (start) tag (the tag is read)
382 * on the input 'file' or the input string 'str' depending on which one is
383 * not NULL (exactly one of them must not be NULL) and returns that string
384 * without the tag. It returns NULL if the tag is not found.
385 * \param[in] file The file where to read up to tag (if not NULL).
386 * \param[in,out] str The string where to read up to tag (if not NULL).
387 * This pointer is updated to reflect the read and points
388 * after the tag in the input string.
389 * \param[in] name The name of the tag to the file reading.
390 * \return The string that has been read from the file.
392 char * osl_util_read_uptotag(FILE * file, char ** str, char * name) {
393 char tag[strlen(name) + 3];
395 sprintf(tag, "<%s>", name);
396 return osl_util_read_uptoflag(file, str, tag);
401 * osl_util_read_uptoendtag function:
402 * this function reads a string up to a given end tag (the end tag is read)
403 * on the input 'file' or the input string 'str' depending on which one is
404 * not NULL (exactly one of them must not be NULL) and returns that string
405 * without the end tag. It returns NULL if the end tag is not found.
406 * \param[in] file The file where to read up to end tag (if not NULL).
407 * \param[in,out] str The string where to read up to end tag (if not NULL).
408 * This pointer is updated to reflect the read and points
409 * after the end tag in the input string.
410 * \param[in] name The name of the end tag to the file reading.
411 * \return The string that has been read from the file.
413 char * osl_util_read_uptoendtag(FILE * file, char ** str, char * name) {
414 char endtag[strlen(name) + 4];
416 sprintf(endtag, "</%s>", name);
417 return osl_util_read_uptoflag(file, str, endtag);
422 * osl_util_tag_content function:
423 * this function returns a freshly allocated string containing the
424 * content, in the given string 'str', between the tag '\<name\>' and
425 * the tag '\</name\>'. If the tag '\<name\>' is not found, it returns NULL.
426 * \param[in] str The string where to find a given content.
427 * \param[in] name The name of the tag we are looking for.
428 * \return The string between '\<name\>' and '\</name\>' in 'str'.
430 char * osl_util_tag_content(char * str, char * name) {
431 int i;
432 char * start;
433 char * stop;
434 char tag[strlen(name) + 3];
435 char endtag[strlen(name) + 4];
436 int size = 0;
437 size_t lentag;
438 char * res = NULL;
440 sprintf(tag, "<%s>", name);
441 sprintf(endtag, "</%s>", name);
443 if (str) {
444 start = str;
445 lentag = strlen(tag);
446 for (; start && *start && strncmp(start, tag, lentag); ++start)
447 continue;
449 // The tag 'tag' was not found.
450 if (! *start)
451 return NULL;
452 start += lentag;
453 stop = start;
454 lentag = strlen(endtag);
455 for (size = 0; *stop && strncmp(stop, endtag, lentag); ++stop, ++size)
456 continue;
458 // the tag 'endtag' was not found.
459 if (! *stop)
460 return NULL;
461 OSL_malloc(res, char *, (size + 1) * sizeof(char));
463 // Copy the chain between the two tags.
464 for (++start, i = 0; start != stop; ++start, ++i)
465 res[i] = *start;
466 res[i] = '\0';
469 return res;
474 * osl_util_safe_strcat function:
475 * this function concatenates the string src to the string *dst
476 * and reallocates *dst if necessary. The current size of the
477 * *dst buffer must be *hwm (high water mark), if there is some
478 * reallocation, this value is updated.
479 * \param[in,out] dst pointer to the destination string (may be reallocated).
480 * \param[in] src string to concatenate to dst.
481 * \param[in,out] hwm pointer to the size of the *dst buffer (may be updated).
483 void osl_util_safe_strcat(char ** dst, char * src, int * hwm) {
485 while ((int)(strlen(*dst) + strlen(src)) >= *hwm) {
486 *hwm += OSL_MAX_STRING;
487 OSL_realloc(*dst, char *, *hwm * sizeof(char));
490 strcat(*dst, src);
495 * \brief String duplicate
497 * osl_util_strdup function:
498 * this function return a copy of the string str.
500 * \param[in] str string to be copied.
502 * \return a copy of the string
504 char * osl_util_strdup(char const * str) {
505 char * dup = NULL;
506 OSL_malloc(dup, char *, (strlen(str) + 1) * sizeof(char));
507 if (dup) { strcpy(dup, str); }
508 return dup;
513 * osl_util_get_precision function:
514 * this function returns the precision defined by the precision environment
515 * variable or the highest available precision if it is not defined.
516 * \return environment precision if defined or highest available precision.
518 int osl_util_get_precision() {
519 int precision = OSL_PRECISION_DP;
520 char * precision_env;
522 #ifdef OSL_GMP_IS_HERE
523 precision = OSL_PRECISION_MP;
524 #endif
526 precision_env = getenv(OSL_PRECISION_ENV);
527 if (precision_env != NULL) {
528 if (!strcmp(precision_env, OSL_PRECISION_ENV_SP))
529 precision = OSL_PRECISION_SP;
530 else if (!strcmp(precision_env, OSL_PRECISION_ENV_DP))
531 precision = OSL_PRECISION_DP;
532 else if (!strcmp(precision_env, OSL_PRECISION_ENV_MP)) {
533 #ifndef OSL_GMP_IS_HERE
534 OSL_warning("$OSL_PRECISION says GMP but osl not compiled with "
535 "GMP support, switching to double precision");
536 precision = OSL_PRECISION_DP;
537 #else
538 precision = OSL_PRECISION_MP;
539 #endif
541 else
542 OSL_warning("bad OSL_PRECISION environment value, see osl's manual");
545 return precision;
550 * osl_util_print_provided function:
551 * this function prints a "provided" boolean in a file (file, possibly stdout),
552 * with a comment title according to the OpenScop specification.
553 * \param[in] file File where the information has to be printed.
554 * \param[in] provided The provided boolean to print.
555 * \param[in] title A string to use as a title for the provided booblean.
557 void osl_util_print_provided(FILE * file, int provided, char * title) {
558 if (provided) {
559 fprintf(file, "# %s provided\n", title);
560 fprintf(file, "1\n");
562 else {
563 fprintf(file, "# %s not provided\n", title);
564 fprintf(file, "0\n\n");
570 * osl_util_identifier_is_here function:
571 * this function returns 1 if the input "identifier" is found at the
572 * "index" position in the "expression" input string, 0 otherwise.
573 * \param[in] expression The input expression.
574 * \param[in] identifier The identifier to look for.
575 * \param[in] index The position in the expression where to look.
576 * \return 1 if the identifier is found at the position in the expression.
578 static
579 int osl_util_identifier_is_here(char * expression, char * identifier,
580 int index) {
581 // If there is no space enough to find the identifier: no.
582 if (strlen(identifier) + index > strlen(expression))
583 return 0;
585 // If there is a character before and it is in [A-Za-z0-9]: no.
586 if ((index > 0) &&
587 (((expression[index - 1] >= 'A') && (expression[index - 1] <= 'Z')) ||
588 ((expression[index - 1] >= 'a') && (expression[index - 1] <= 'z')) ||
589 ((expression[index - 1] >= '0') && (expression[index - 1] <= '9'))))
590 return 0;
592 // If there is a character after and it is in [A-Za-z0-9]: no.
593 if ((strlen(identifier) + index < strlen(expression)) &&
594 (((expression[strlen(identifier) + index] >= 'A') &&
595 (expression[strlen(identifier) + index] <= 'Z')) ||
596 ((expression[strlen(identifier) + index] >= 'a') &&
597 (expression[strlen(identifier) + index] <= 'z')) ||
598 ((expression[strlen(identifier) + index] >= '0') &&
599 (expression[strlen(identifier) + index] <= '9'))))
600 return 0;
602 // If the identifier string is not here: no.
603 if (strncmp(expression + index, identifier, strlen(identifier)))
604 return 0;
606 return 1;
611 * osl_util_lazy_isolated_identifier function:
612 * this function returns 1 if the identifier at the "index" position in the
613 * "expression" is guaranteed not to need parenthesis around is we
614 * substitute it with anything. For instance the identifier "i" can be
615 * always substituted in "A[i]" with no need of parenthesis but not in
616 * "A[2*i]". This function is lazy in the sense that it just check obvious
617 * cases, not all of them. The identifier must already be at the indicated
618 * position, this function does not check that.
619 * \param[in] expression The input expression.
620 * \param[in] identifier The identifier to check.
621 * \param[in] index The position of the identifier in the expression.
622 * \return 1 if the identifier is isolated, 0 if unsure.
624 static
625 int osl_util_lazy_isolated_identifier(char * expression, char * identifier,
626 int index) {
627 int look;
629 // If the first non-space character before is not in [\[(,\+=]: no.
630 look = index - 1;
631 while (look >= 0) {
632 if (isspace(expression[look]))
633 look--;
634 else
635 break;
638 if ((look >= 0) &&
639 (expression[look] != '[') &&
640 (expression[look] != '(') &&
641 (expression[look] != '+') &&
642 (expression[look] != '=') &&
643 (expression[look] != ','))
644 return 0;
646 // If the first non-space character after is not in [\]),;\+]: no.
647 look = index + strlen(identifier);
648 while (look < (int)strlen(expression)) {
649 if (isspace(expression[look]))
650 look++;
651 else
652 break;
655 if ((look < (int)strlen(expression)) &&
656 (expression[look] != ']') &&
657 (expression[look] != ')') &&
658 (expression[look] != '+') &&
659 (expression[look] != ',') &&
660 (expression[look] != ';'))
661 return 0;
663 return 1;
668 * osl_util_identifier_substitution function:
669 * this function replaces some identifiers in an input expression string and
670 * returns the final string. The list of identifiers to replace are provided
671 * as an array of strings. They are replaced from the input string with the
672 * new substring "@i@" or "(@i@)" where i is the rank of the identifier in the
673 * array of identifiers. The parentheses are added when it is not obvious that
674 * the identifier can be replaced with an arbitrary expression without the
675 * need of parentheses. For instance, let us consider the input expression
676 * "C[i+j]+=A[2*i]*B[j];" and the array of strings {"i", "j"}: the resulting
677 * string would be "C[@0@+@1@]+=A[2*(@0@)]*B[@1@];".
678 * \param[in] expression The original expression.
679 * \param[in] identifiers NULL-terminated array of identifiers.
680 * \return A new string where the ith identifier is replaced by \@i\@.
682 char * osl_util_identifier_substitution(char * expression,
683 char ** identifiers) {
684 size_t index;
685 int j, found;
686 int high_water_mark = OSL_MAX_STRING;
687 char buffer[OSL_MAX_STRING];
688 char * string;
690 OSL_malloc(string, char *, high_water_mark * sizeof(char));
691 string[0] = '\0';
693 index = 0;
694 while (index < strlen(expression)) {
695 j = 0;
696 found = 0;
697 while (identifiers[j] != NULL) {
698 if (osl_util_identifier_is_here(expression, identifiers[j], index)) {
699 if (osl_util_lazy_isolated_identifier(expression,identifiers[j],index))
700 sprintf(buffer, "@%d@", j);
701 else
702 sprintf(buffer, "(@%d@)", j);
703 osl_util_safe_strcat(&string, buffer, &high_water_mark);
704 index += strlen(identifiers[j]);
705 found = 1;
706 break;
708 j++;
710 if (!found) {
711 sprintf(buffer, "%c", expression[index]);
712 osl_util_safe_strcat(&string, buffer, &high_water_mark);
713 index++;
717 return string;