1 /*
2  * Copyright (C) by Argonne National Laboratory
3  *     See COPYRIGHT in top-level directory
4  */
5 
6 #include "mpl.h"
7 
8 #ifdef MPL_HAVE_MATH_H
9 #include <math.h>
10 #endif
11 /* ctype is needed for isspace and isascii (isspace is only defined for
12    values on which isascii returns true). */
13 #include <ctype.h>
14 
encode_buffer(char * dest,int dest_length,const char * src,int src_length,int * num_encoded)15 static int encode_buffer(char *dest, int dest_length, const char *src,
16                          int src_length, int *num_encoded)
17 {
18     int num_used;
19     int n = 0;
20     if (src_length == 0) {
21         if (dest_length > 2) {
22             *dest = MPL_STR_QUOTE_CHAR;
23             dest++;
24             *dest = MPL_STR_QUOTE_CHAR;
25             dest++;
26             *dest = '\0';
27             *num_encoded = 0;
28             return MPL_SUCCESS;
29         } else {
30             return MPL_ERR_STR_TRUNCATED;
31         }
32     }
33     while (src_length && dest_length) {
34         num_used = MPL_snprintf(dest, dest_length, "%02X", (unsigned char) *src);
35         if (num_used < 0) {
36             *num_encoded = n;
37             return MPL_ERR_STR_TRUNCATED;
38         }
39         /*MPL_DBG_MSG_FMT(STRING,VERBOSE,(MPL_DBG_FDEST," %c = %c%c",
40          * ch, dest[0], dest[1])); */
41         dest += num_used;
42         dest_length -= num_used;
43         src++;
44         n++;
45         src_length--;
46     }
47     *num_encoded = n;
48     return src_length ? MPL_ERR_STR_TRUNCATED : MPL_SUCCESS;
49 }
50 
decode_buffer(const char * str,char * dest,int length,int * num_decoded)51 static int decode_buffer(const char *str, char *dest, int length, int *num_decoded)
52 {
53     char hex[3];
54     int value;
55     int n = 0;
56 
57     if (str == NULL || dest == NULL || num_decoded == NULL)
58         return MPL_ERR_STR_FAIL;
59     if (length < 1) {
60         *num_decoded = 0;
61         if (*str == '\0')
62             return MPL_SUCCESS;
63         return MPL_ERR_STR_TRUNCATED;
64     }
65     if (*str == MPL_STR_QUOTE_CHAR)
66         str++;
67     hex[2] = '\0';
68     while (*str != '\0' && *str != MPL_STR_SEPAR_CHAR && *str != MPL_STR_QUOTE_CHAR && length) {
69         hex[0] = *str;
70         str++;
71         hex[1] = *str;
72         str++;
73         if (0 == sscanf(hex, "%X", &value))
74             return MPL_ERR_STR_TRUNCATED;
75         *dest = (char) value;
76         /*MPL_DBG_MSG_FMT(STRING,VERBOSE,(MPL_DBG_FDEST," %s = %c",
77          * hex, *dest)); */
78         dest++;
79         n++;
80         length--;
81     }
82     *num_decoded = n;
83     if (length == 0) {
84         if (*str != '\0' && *str != MPL_STR_SEPAR_CHAR && *str != MPL_STR_QUOTE_CHAR)
85             return MPL_ERR_STR_TRUNCATED;
86     }
87     return MPL_SUCCESS;
88 }
89 
first_token(const char * str)90 static const char *first_token(const char *str)
91 {
92     if (str == NULL)
93         return NULL;
94     /* isspace is defined only if isascii is true */
95     while (/*isascii(*str) && isspace(*str) */ *str == MPL_STR_SEPAR_CHAR)
96         str++;
97     if (*str == '\0')
98         return NULL;
99     return str;
100 }
101 
next_token(const char * str)102 static const char *next_token(const char *str)
103 {
104     if (str == NULL)
105         return NULL;
106     str = first_token(str);
107     if (str == NULL)
108         return NULL;
109     if (*str == MPL_STR_QUOTE_CHAR) {
110         /* move over string */
111         str++;  /* move over the first quote */
112         if (*str == '\0')
113             return NULL;
114         while (*str != MPL_STR_QUOTE_CHAR) {
115             /* move until the last quote, ignoring escaped quotes */
116             if (*str == MPL_STR_ESCAPE_CHAR) {
117                 str++;
118                 if (*str == MPL_STR_QUOTE_CHAR)
119                     str++;
120             } else {
121                 str++;
122             }
123             if (*str == '\0')
124                 return NULL;
125         }
126         str++;  /* move over the last quote */
127     } else {
128         if (*str == MPL_STR_DELIM_CHAR) {
129             /* move over the DELIM token */
130             str++;
131         } else {
132             /* move over literal */
133             while (/*(isascii(*str) &&
134                          * !isspace(*str)) && */
135                       *str != MPL_STR_SEPAR_CHAR && *str != MPL_STR_DELIM_CHAR && *str != '\0')
136                 str++;
137         }
138     }
139     return first_token(str);
140 }
141 
compare_token(const char * token,const char * str)142 static int compare_token(const char *token, const char *str)
143 {
144     if (token == NULL || str == NULL)
145         return -1;
146 
147     if (*token == MPL_STR_QUOTE_CHAR) {
148         /* compare quoted strings */
149         token++;        /* move over the first quote */
150         /* compare characters until reaching the end of the string or the
151          * end quote character */
152         for (;;) {
153             if (*token == MPL_STR_ESCAPE_CHAR) {
154                 if (*(token + 1) == MPL_STR_QUOTE_CHAR) {
155                     /* move over the escape character if the next character
156                      * is a quote character */
157                     token++;
158                 }
159                 if (*token != *str)
160                     break;
161             } else {
162                 if (*token != *str || *token == MPL_STR_QUOTE_CHAR)
163                     break;
164             }
165             if (*str == '\0')
166                 break;
167             token++;
168             str++;
169         }
170         if (*str == '\0' && *token == MPL_STR_QUOTE_CHAR)
171             return 0;
172         if (*token == MPL_STR_QUOTE_CHAR)
173             return 1;
174         if (*str < *token)
175             return -1;
176         return 1;
177     }
178 
179     /* compare DELIM token */
180     if (*token == MPL_STR_DELIM_CHAR) {
181         if (*str == MPL_STR_DELIM_CHAR) {
182             str++;
183             if (*str == '\0')
184                 return 0;
185             return 1;
186         }
187         if (*token < *str)
188             return -1;
189         return 1;
190     }
191 
192     /* compare literals */
193     while (*token == *str &&
194            *str != '\0' && *token != MPL_STR_DELIM_CHAR && (*token != MPL_STR_SEPAR_CHAR)) {
195         token++;
196         str++;
197     }
198     if ((*str == '\0') &&
199         (*token == MPL_STR_DELIM_CHAR || (*token == MPL_STR_SEPAR_CHAR) || *token == '\0'))
200         return 0;
201     if (*token == MPL_STR_DELIM_CHAR || (*token == MPL_STR_SEPAR_CHAR) || *token < *str)
202         return -1;
203     return 1;
204 }
205 
206 
token_copy(const char * token,char * str,int maxlen)207 static int token_copy(const char *token, char *str, int maxlen)
208 {
209     /* check parameters */
210     if (token == NULL || str == NULL)
211         return MPL_ERR_STR_FAIL;
212 
213     /* check special buffer lengths */
214     if (maxlen < 1)
215         return MPL_ERR_STR_FAIL;
216     if (maxlen == 1) {
217         *str = '\0';
218         return (str[0] == '\0') ? MPL_SUCCESS : MPL_ERR_STR_TRUNCATED;
219     }
220 
221     /* cosy up to the token */
222     token = first_token(token);
223     if (token == NULL) {
224         *str = '\0';
225         return MPL_SUCCESS;
226     }
227 
228     if (*token == MPL_STR_DELIM_CHAR) {
229         /* copy the special deliminator token */
230         str[0] = MPL_STR_DELIM_CHAR;
231         str[1] = '\0';
232         return MPL_SUCCESS;
233     }
234 
235     if (*token == MPL_STR_QUOTE_CHAR) {
236         /* quoted copy */
237         token++;        /* move over the first quote */
238         do {
239             if (*token == MPL_STR_ESCAPE_CHAR) {
240                 if (*(token + 1) == MPL_STR_QUOTE_CHAR)
241                     token++;
242                 *str = *token;
243             } else {
244                 if (*token == MPL_STR_QUOTE_CHAR) {
245                     *str = '\0';
246                     return MPL_SUCCESS;
247                 }
248                 *str = *token;
249             }
250             str++;
251             token++;
252             maxlen--;
253         } while (maxlen);
254         /* we've run out of destination characters so back up and null
255          * terminate the string */
256         str--;
257         *str = '\0';
258         return MPL_ERR_STR_TRUNCATED;
259     }
260 
261     /* literal copy */
262     while (*token != MPL_STR_DELIM_CHAR &&
263            (*token != MPL_STR_SEPAR_CHAR) && *token != '\0' && maxlen) {
264         *str = *token;
265         str++;
266         token++;
267         maxlen--;
268     }
269     if (maxlen) {
270         *str = '\0';
271         return MPL_SUCCESS;
272     }
273     str--;
274     *str = '\0';
275     return MPL_ERR_STR_TRUNCATED;
276 }
277 
278 /*@ MPL_str_get_string_arg - Extract an option from a string with a
279   maximum length
280 
281 Input Parameters:
282 +   str - Source string
283 .   key - key
284 -   maxlen - Maximum total length of 'val'
285 
286 Output Parameters:
287 .   val - output string
288 
289     Return value:
290     MPL_SUCCESS, MPL_ERR_STR_NOMEM, MPL_ERR_STR
291 
292     Notes:
293     This routine searches for a "key = value" entry in a string
294 
295   Module:
296   Utility
297   @*/
MPL_str_get_string_arg(const char * str,const char * flag,char * val,int maxlen)298 int MPL_str_get_string_arg(const char *str, const char *flag, char *val, int maxlen)
299 {
300     if (maxlen < 1)
301         return MPL_ERR_STR_FAIL;
302 
303     /* line up with the first token */
304     str = first_token(str);
305     if (str == NULL)
306         return MPL_ERR_STR_FAIL;
307 
308     /* This loop will match the first instance of "flag = value" in the string. */
309     do {
310         if (compare_token(str, flag) == 0) {
311             str = next_token(str);
312             if (compare_token(str, MPL_STR_DELIM_STR) == 0) {
313                 str = next_token(str);
314                 if (str == NULL)
315                     return MPL_ERR_STR_FAIL;
316                 return token_copy(str, val, maxlen);
317             }
318         } else {
319             str = next_token(str);
320         }
321     } while (str);
322     return MPL_ERR_STR_FAIL;
323 }
324 
325 /*@ MPL_str_get_binary_arg - Extract an option from a string with a maximum
326   length
327 
328 Input Parameters:
329 +   str - Source string
330 .   key - key
331 -   maxlen - Maximum total length of 'buffer'
332 
333 Output Parameters:
334 +   buffer - output buffer
335 -   out_length - output length
336 
337     Return value:
338     MPL_SUCCESS, MPL_ERR_STR_NOMEM, MPL_ERR_STR
339 
340     Notes:
341     This routine searches for a "key = value" entry in a string and decodes
342     the value
343     back to binary data.  The data must have been encoded with
344     MPL_str_add_binary_arg.
345 
346   Module:
347   Utility
348   @*/
MPL_str_get_binary_arg(const char * str,const char * flag,char * buffer,int maxlen,int * out_length)349 int MPL_str_get_binary_arg(const char *str, const char *flag, char *buffer,
350                            int maxlen, int *out_length)
351 {
352     if (maxlen < 1)
353         return MPL_ERR_STR_FAIL;
354 
355     /* line up with the first token */
356     str = first_token(str);
357     if (str == NULL)
358         return MPL_ERR_STR_FAIL;
359 
360     /* This loop will match the first instance of "flag = value" in the string. */
361     do {
362         if (compare_token(str, flag) == 0) {
363             str = next_token(str);
364             if (compare_token(str, MPL_STR_DELIM_STR) == 0) {
365                 str = next_token(str);
366                 if (str == NULL)
367                     return MPL_ERR_STR_FAIL;
368                 return decode_buffer(str, buffer, maxlen, out_length);
369             }
370         } else {
371             str = next_token(str);
372         }
373     } while (str);
374     return MPL_ERR_STR_FAIL;
375 }
376 
377 /*@ MPL_str_get_int_arg - Extract an option from a string
378 
379 Input Parameters:
380 +   str - Source string
381 -   key - key
382 
383 Output Parameters:
384 .   val_ptr - pointer to the output integer
385 
386     Return value:
387     MPL_SUCCESS, MPL_ERR_STR_NOMEM, MPL_ERR_STR
388 
389     Notes:
390     This routine searches for a "key = value" entry in a string and decodes the value
391     back to an int.
392 
393   Module:
394   Utility
395   @*/
MPL_str_get_int_arg(const char * str,const char * flag,int * val_ptr)396 int MPL_str_get_int_arg(const char *str, const char *flag, int *val_ptr)
397 {
398     int result;
399     char int_str[12];
400 
401     result = MPL_str_get_string_arg(str, flag, int_str, 12);
402     if (result == MPL_SUCCESS) {
403         *val_ptr = atoi(int_str);
404         return MPL_SUCCESS;
405     }
406     return result;
407 }
408 
409 /* quoted_printf does not NULL terminate the string if maxlen is reached */
quoted_printf(char * str,int maxlen,const char * val)410 static int quoted_printf(char *str, int maxlen, const char *val)
411 {
412     int count = 0;
413     if (maxlen < 1)
414         return 0;
415     *str = MPL_STR_QUOTE_CHAR;
416     str++;
417     maxlen--;
418     count++;
419     while (maxlen) {
420         if (*val == '\0')
421             break;
422         if (*val == MPL_STR_QUOTE_CHAR) {
423             *str = MPL_STR_ESCAPE_CHAR;
424             str++;
425             maxlen--;
426             count++;
427             if (maxlen == 0)
428                 return count;
429         }
430         *str = *val;
431         str++;
432         maxlen--;
433         count++;
434         val++;
435     }
436     if (maxlen) {
437         *str = MPL_STR_QUOTE_CHAR;
438         str++;
439         maxlen--;
440         count++;
441         if (maxlen == 0)
442             return count;
443         *str = '\0';
444     }
445     return count;
446 }
447 
448 /*@ MPL_str_add_string - Add a string to a string
449 
450 Input Parameters:
451 +   str_ptr - pointer to the destination string
452 .   maxlen_ptr - pointer to the maximum length of '*str_ptr'
453 -   val - string to add
454 
455 Output Parameters:
456 +   str_ptr - The string pointer is updated to the next available location in
457     the string
458 -   maxlen_ptr - maxlen is decremented by the amount str_ptr is incremented
459 
460     Return value:
461     MPL_SUCCESS, MPL_ERR_STR_NOMEM, MPL_ERR_STR
462 
463     Notes:
464     This routine adds a string to a string in such a way that
465     MPL_str_get_string can
466     retreive the same string back.  It takes into account spaces and quote
467     characters.
468     The string pointer is updated to the start of the next string in the
469     string and maxlen is updated accordingly.
470 
471   Module:
472   Utility
473   @*/
MPL_str_add_string(char ** str_ptr,int * maxlen_ptr,const char * val)474 int MPL_str_add_string(char **str_ptr, int *maxlen_ptr, const char *val)
475 {
476     int num_chars;
477     char *str;
478     int maxlen;
479 
480     str = *str_ptr;
481     maxlen = *maxlen_ptr;
482 
483     if (strchr(val, MPL_STR_SEPAR_CHAR) ||
484         strchr(val, MPL_STR_QUOTE_CHAR) || strchr(val, MPL_STR_DELIM_CHAR)) {
485         num_chars = quoted_printf(str, maxlen, val);
486         if (num_chars == maxlen) {
487             /* truncation, cleanup string */
488             *str = '\0';
489             return -1;
490         }
491         if (num_chars < maxlen - 1) {
492             str[num_chars] = MPL_STR_SEPAR_CHAR;
493             str[num_chars + 1] = '\0';
494             num_chars++;
495         } else {
496             str[num_chars] = '\0';
497         }
498     } else {
499         if (*val == '\0') {
500             num_chars = MPL_snprintf(str, maxlen, MPL_STR_QUOTE_STR MPL_STR_QUOTE_STR /*"\"\"" */);
501         } else {
502             num_chars = MPL_snprintf(str, maxlen, "%s%c", val, MPL_STR_SEPAR_CHAR);
503         }
504         if (num_chars == maxlen) {
505             *str = '\0';
506             return -1;
507         }
508     }
509     *str_ptr += num_chars;
510     *maxlen_ptr -= num_chars;
511     return 0;
512 }
513 
514 /*@ MPL_str_get_string - Get the next string from a string
515 
516 Input Parameters:
517 +   str_ptr - pointer to the destination string
518 -   maxlen_ptr - pointer to the maximum length of '*str_ptr'
519 
520 Output Parameters:
521 +   str_ptr - location of the next string
522 -   val - location to store the string
523 
524     Return value:
525     MPL_SUCCESS, MPL_ERR_STR_NOMEM, MPL_ERR_STR
526 
527     Return Value:
528     The return value is 0 for success, -1 for insufficient buffer space, and
529     1 for failure.
530 
531     Notes:
532     This routine gets a string that was previously added by
533     MPL_str_add_string.
534     It takes into account spaces and quote characters. The string pointer is
535     updated to the start of the next string in the string.
536 
537   Module:
538   Utility
539   @*/
MPL_str_get_string(char ** str_ptr,char * val,int maxlen)540 int MPL_str_get_string(char **str_ptr, char *val, int maxlen)
541 {
542     int result;
543     char *str;
544 
545     if (str_ptr == NULL) {
546         return -2;
547     }
548 
549     str = *str_ptr;
550 
551     if (maxlen < 1) {
552         return 0;
553     }
554 
555     /* line up with the first token */
556     str = (char *) first_token(str);
557     if (str == NULL) {
558         return 0;
559     }
560 
561     /* copy the token */
562     result = token_copy(str, val, maxlen);
563     if (result == MPL_SUCCESS) {
564         str = (char *) next_token(str);
565         *str_ptr = str;
566         return 0;
567     } else if (result == MPL_ERR_STR_TRUNCATED) {
568         return -1;
569     }
570 
571     /* failure */
572     return -2;
573 }
574 
575 /*@ MPL_str_add_string_arg - Add an option to a string with a maximum length
576 
577 Input Parameters:
578 +   str_ptr - Pointer to the destination string
579 .   maxlen_ptr - Pointer to the maximum total length of '*str_ptr'
580 .   key - key
581 -   val - input string
582 
583 Output Parameters:
584 +   str_ptr - The string pointer is updated to the next available location in
585     the string
586 -   maxlen_ptr - maxlen is reduced by the number of characters written
587 
588     Return value:
589     MPL_SUCCESS, MPL_ERR_STR_NOMEM, MPL_ERR_STR
590 
591     Notes:
592     This routine adds a string option to a string in the form "key = value".
593 
594   Module:
595   Utility
596   @*/
MPL_str_add_string_arg(char ** str_ptr,int * maxlen_ptr,const char * flag,const char * val)597 int MPL_str_add_string_arg(char **str_ptr, int *maxlen_ptr, const char *flag, const char *val)
598 {
599     int num_chars;
600     char **orig_str_ptr;
601 
602     if (maxlen_ptr == NULL)
603         return MPL_ERR_STR_FAIL;
604 
605     orig_str_ptr = str_ptr;
606 
607     if (*maxlen_ptr < 1)
608         return MPL_ERR_STR_FAIL;
609 
610     /* add the flag */
611     if (strstr(flag, MPL_STR_SEPAR_STR) || strstr(flag, MPL_STR_DELIM_STR) ||
612         flag[0] == MPL_STR_QUOTE_CHAR) {
613         num_chars = quoted_printf(*str_ptr, *maxlen_ptr, flag);
614     } else {
615         num_chars = MPL_snprintf(*str_ptr, *maxlen_ptr, "%s", flag);
616     }
617     *maxlen_ptr = *maxlen_ptr - num_chars;
618     if (*maxlen_ptr < 1) {
619         MPL_DBG_MSG_S(MPIR_DBG_STRING, VERBOSE, "partial argument added to string: '%s'", *str_ptr);
620         **str_ptr = '\0';
621         return MPL_ERR_STR_NOMEM;
622     }
623     *str_ptr = *str_ptr + num_chars;
624 
625     /* add the deliminator character */
626     **str_ptr = MPL_STR_DELIM_CHAR;
627     *str_ptr = *str_ptr + 1;
628     *maxlen_ptr = *maxlen_ptr - 1;
629 
630     /* add the value string */
631     if (strstr(val, MPL_STR_SEPAR_STR) || strstr(val, MPL_STR_DELIM_STR) ||
632         val[0] == MPL_STR_QUOTE_CHAR) {
633         num_chars = quoted_printf(*str_ptr, *maxlen_ptr, val);
634     } else {
635         if (*val == '\0') {
636             num_chars = MPL_snprintf(*str_ptr, *maxlen_ptr,
637                                      MPL_STR_QUOTE_STR MPL_STR_QUOTE_STR /*"\"\"" */);
638         } else {
639             num_chars = MPL_snprintf(*str_ptr, *maxlen_ptr, "%s", val);
640         }
641     }
642     *str_ptr = *str_ptr + num_chars;
643     *maxlen_ptr = *maxlen_ptr - num_chars;
644     if (*maxlen_ptr < 2) {
645         MPL_DBG_MSG_S(MPIR_DBG_STRING, VERBOSE, "partial argument added to string: '%s'", *str_ptr);
646         **orig_str_ptr = '\0';
647         return MPL_ERR_STR_NOMEM;
648     }
649 
650     /* add the trailing space */
651     **str_ptr = MPL_STR_SEPAR_CHAR;
652     *str_ptr = *str_ptr + 1;
653     **str_ptr = '\0';
654     *maxlen_ptr = *maxlen_ptr - 1;
655 
656     return MPL_SUCCESS;
657 }
658 
659 /*@ MPL_str_add_int_arg - Add an option to a string with a maximum length
660 
661 Input Parameters:
662 +   str_ptr - Pointer to the destination string
663 .   maxlen_ptr - Pointer to the maximum total length of '*str_ptr'
664 .   key - key
665 -   val - input integer
666 
667 Output Parameters:
668 +   str_ptr - The string pointer is updated to the next available location in
669     the string
670 -   maxlen_ptr - maxlen is reduced by the number of characters written
671 
672     Return value:
673     MPL_SUCCESS, MPL_ERR_STR_NOMEM, MPL_ERR_STR
674 
675     Notes:
676     This routine adds an integer option to a string in the form "key = value".
677 
678   Module:
679   Utility
680   @*/
MPL_str_add_int_arg(char ** str_ptr,int * maxlen_ptr,const char * flag,int val)681 int MPL_str_add_int_arg(char **str_ptr, int *maxlen_ptr, const char *flag, int val)
682 {
683     char val_str[12];
684     MPL_snprintf(val_str, 12, "%d", val);
685     return MPL_str_add_string_arg(str_ptr, maxlen_ptr, flag, val_str);
686 }
687 
688 /*@ MPL_str_add_binary_arg - Add an option to a string with a maximum length
689 
690 Input Parameters:
691 +   str_ptr - Pointer to the destination string
692 .   maxlen_ptr - Pointer to the maximum total length of '*str_ptr'
693 .   key - key
694 .   val - input data
695 -   length - length of the input data
696 
697 Output Parameters:
698 +   str_ptr - The string pointer is updated to the next available location in
699     the string
700 -   maxlen_ptr - maxlen is reduced by the number of characters written
701 
702     Return value:
703     MPL_SUCCESS, MPL_ERR_STR_NOMEM, MPL_ERR_STR
704 
705     Notes:
706     This routine encodes binary data into a string option in the form
707     "key = encoded_value".
708 
709   Module:
710   Utility
711   @*/
MPL_str_add_binary_arg(char ** str_ptr,int * maxlen_ptr,const char * flag,const char * buffer,int length)712 int MPL_str_add_binary_arg(char **str_ptr, int *maxlen_ptr, const char *flag,
713                            const char *buffer, int length)
714 {
715     int result;
716     int num_chars;
717     char **orig_str_ptr;
718 
719     if (maxlen_ptr == NULL)
720         return MPL_ERR_STR_FAIL;
721 
722     orig_str_ptr = str_ptr;
723 
724     if (*maxlen_ptr < 1)
725         return MPL_ERR_STR_FAIL;
726 
727     /* add the flag */
728     if (strstr(flag, MPL_STR_SEPAR_STR) || strstr(flag, MPL_STR_DELIM_STR) ||
729         flag[0] == MPL_STR_QUOTE_CHAR) {
730         num_chars = quoted_printf(*str_ptr, *maxlen_ptr, flag);
731     } else {
732         num_chars = MPL_snprintf(*str_ptr, *maxlen_ptr, "%s", flag);
733     }
734     *maxlen_ptr = *maxlen_ptr - num_chars;
735     if (*maxlen_ptr < 1) {
736         MPL_DBG_MSG_S(MPIR_DBG_STRING, VERBOSE, "partial argument added to string: '%s'", *str_ptr);
737         **str_ptr = '\0';
738         return MPL_ERR_STR_NOMEM;
739     }
740     *str_ptr = *str_ptr + num_chars;
741 
742     /* add the deliminator character */
743     **str_ptr = MPL_STR_DELIM_CHAR;
744     *str_ptr = *str_ptr + 1;
745     *maxlen_ptr = *maxlen_ptr - 1;
746 
747     /* add the value string */
748     result = encode_buffer(*str_ptr, *maxlen_ptr, buffer, length, &num_chars);
749     if (result != MPL_SUCCESS) {
750         **orig_str_ptr = '\0';
751         return result;
752     }
753     num_chars = num_chars * 2;  /* the encoding function turns one source
754                                  * character into two destination characters */
755     *str_ptr = *str_ptr + num_chars;
756     *maxlen_ptr = *maxlen_ptr - num_chars;
757     if (*maxlen_ptr < 2) {
758         MPL_DBG_MSG_S(MPIR_DBG_STRING, VERBOSE, "partial argument added to string: '%s'", *str_ptr);
759         **orig_str_ptr = '\0';
760         return MPL_ERR_STR_NOMEM;
761     }
762 
763     /* add the trailing space */
764     **str_ptr = MPL_STR_SEPAR_CHAR;
765     *str_ptr = *str_ptr + 1;
766     **str_ptr = '\0';
767     *maxlen_ptr = *maxlen_ptr - 1;
768 
769     return MPL_SUCCESS;
770 }
771