Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Kill old $Id$ command dating from CVS
[simgrid.git] / src / xbt / xbt_str.c
1 /* xbt_str.c - various helping functions to deal with strings               */
2
3 /* Copyright (C) 2005-2008 The SimGrid Team.                                */
4 /* All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package.
8  */
9
10 #include "portable.h"
11 #include "xbt/misc.h"
12 #include "xbt/sysdep.h"
13 #include "xbt/str.h"            /* headers of these functions */
14 #include "xbt/strbuff.h"
15 #include "xbt/matrix.h"         /* for the diff */
16
17 /**  @brief Strip whitespace (or other characters) from the end of a string.
18  *
19  * Strips the whitespaces from the end of s.
20  * By default (when char_list=NULL), these characters get stripped:
21  *
22  *      - " "           (ASCII 32       (0x20)) space.
23  *      - "\t"          (ASCII 9        (0x09)) tab.
24  *      - "\n"          (ASCII 10       (0x0A)) line feed.
25  *      - "\r"          (ASCII 13       (0x0D)) carriage return.
26  *      - "\0"          (ASCII 0        (0x00)) NULL.
27  *      - "\x0B"        (ASCII 11       (0x0B)) vertical tab.
28  *
29  * @param s The string to strip. Modified in place.
30  * @param char_list A string which contains the characters you want to strip.
31  *
32  */
33 void xbt_str_rtrim(char *s, const char *char_list)
34 {
35   char *cur = s;
36   const char *__char_list = " \t\n\r\x0B";
37   char white_char[256] = { 1, 0 };
38
39   if (!s)
40     return;
41
42   if (!char_list) {
43     while (*__char_list) {
44       white_char[(unsigned char) *__char_list++] = 1;
45     }
46   } else {
47     while (*char_list) {
48       white_char[(unsigned char) *char_list++] = 1;
49     }
50   }
51
52   while (*cur)
53     ++cur;
54
55   while ((cur >= s) && white_char[(unsigned char) *cur])
56     --cur;
57
58   *++cur = '\0';
59 }
60
61 /**  @brief Strip whitespace (or other characters) from the beginning of a string.
62  *
63  * Strips the whitespaces from the begining of s.
64  * By default (when char_list=NULL), these characters get stripped:
65  *
66  *      - " "           (ASCII 32       (0x20)) space.
67  *      - "\t"          (ASCII 9        (0x09)) tab.
68  *      - "\n"          (ASCII 10       (0x0A)) line feed.
69  *      - "\r"          (ASCII 13       (0x0D)) carriage return.
70  *      - "\0"          (ASCII 0        (0x00)) NULL.
71  *      - "\x0B"        (ASCII 11       (0x0B)) vertical tab.
72  *
73  * @param s The string to strip. Modified in place.
74  * @param char_list A string which contains the characters you want to strip.
75  *
76  */
77 void xbt_str_ltrim(char *s, const char *char_list)
78 {
79   char *cur = s;
80   const char *__char_list = " \t\n\r\x0B";
81   char white_char[256] = { 1, 0 };
82
83   if (!s)
84     return;
85
86   if (!char_list) {
87     while (*__char_list) {
88       white_char[(unsigned char) *__char_list++] = 1;
89     }
90   } else {
91     while (*char_list) {
92       white_char[(unsigned char) *char_list++] = 1;
93     }
94   }
95
96   while (*cur && white_char[(unsigned char) *cur])
97     ++cur;
98
99   memmove(s, cur, strlen(cur) + 1);
100 }
101
102 /**  @brief Strip whitespace (or other characters) from the end and the begining of a string.
103  *
104  * Strips the whitespaces from both the beginning and the end of s.
105  * By default (when char_list=NULL), these characters get stripped:
106  *
107  *      - " "           (ASCII 32       (0x20)) space.
108  *      - "\t"          (ASCII 9        (0x09)) tab.
109  *      - "\n"          (ASCII 10       (0x0A)) line feed.
110  *      - "\r"          (ASCII 13       (0x0D)) carriage return.
111  *      - "\0"          (ASCII 0        (0x00)) NULL.
112  *      - "\x0B"        (ASCII 11       (0x0B)) vertical tab.
113  *
114  * @param s The string to strip.
115  * @param char_list A string which contains the characters you want to strip.
116  *
117  */
118 void xbt_str_trim(char *s, const char *char_list)
119 {
120
121   if (!s)
122     return;
123
124   xbt_str_rtrim(s, char_list);
125   xbt_str_ltrim(s, char_list);
126 }
127
128 /**  @brief Replace double whitespaces (but no other characters) from the string.
129  *
130  * The function modifies the string so that each time that several spaces appear,
131  * they are replaced by a single space. It will only do so for spaces (ASCII 32, 0x20).
132  *
133  * @param s The string to strip. Modified in place.
134  *
135  */
136 void xbt_str_strip_spaces(char *s)
137 {
138   char *p = s;
139   int e = 0;
140
141   if (!s)
142     return;
143
144   while (1) {
145     if (!*p)
146       goto end;
147
148     if (*p != ' ')
149       break;
150
151     p++;
152   }
153
154   e = 1;
155
156   do {
157     if (e)
158       *s++ = *p;
159
160     if (!*++p)
161       goto end;
162
163     if (e ^ (*p != ' '))
164       if ((e = !e))
165         *s++ = ' ';
166   } while (1);
167
168 end:
169   *s = '\0';
170 }
171
172 /** @brief Substitutes a char for another in a string
173  *
174  * @param str the string to modify
175  * @param from char to search
176  * @param to char to put instead
177  * @param amount amount of changes to do (=0 means all)
178  */
179 void xbt_str_subst(char *str, char from, char to, int occurence)
180 {
181   char *p = str;
182   while (*p != '\0') {
183     if (*p == from) {
184       *p = to;
185       if (occurence == 1)
186         return;
187       occurence--;
188     }
189     p++;
190   }
191 }
192
193 /** @brief Replaces a set of variables by their values
194  *
195  * @param str where to apply the change
196  * @param patterns what to change
197  * @return The string modified
198  *
199  * Check xbt_strbuff_varsubst() for more details, and remember that the string may be reallocated (moved) in the process.
200  */
201
202 char *xbt_str_varsubst(char *str, xbt_dict_t patterns)
203 {
204   xbt_strbuff_t buff = xbt_strbuff_new_from(str);
205   char *res;
206   xbt_strbuff_varsubst(buff, patterns);
207   res = buff->data;
208   xbt_strbuff_free_container(buff);
209   return res;
210 }
211
212
213 /** @brief Splits a string into a dynar of strings
214  *
215  * @param s: the string to split
216  * @param sep: a string of all chars to consider as separator.
217  *
218  * By default (with sep=NULL), these characters are used as separator:
219  *
220  *      - " "           (ASCII 32       (0x20)) space.
221  *      - "\t"          (ASCII 9        (0x09)) tab.
222  *      - "\n"          (ASCII 10       (0x0A)) line feed.
223  *      - "\r"          (ASCII 13       (0x0D)) carriage return.
224  *      - "\0"          (ASCII 0        (0x00)) NULL.
225  *      - "\x0B"        (ASCII 11       (0x0B)) vertical tab.
226  */
227
228 xbt_dynar_t xbt_str_split(const char *s, const char *sep)
229 {
230   xbt_dynar_t res = xbt_dynar_new(sizeof(char *), &xbt_free_ref);
231   const char *p, *q;
232   int done;
233   const char *sep_dflt = " \t\n\r\x0B";
234   char is_sep[256] = { 1, 0 };
235
236   /* check what are the separators */
237   memset(is_sep, 0, sizeof(is_sep));
238   if (!sep) {
239     while (*sep_dflt)
240       is_sep[(unsigned char) *sep_dflt++] = 1;
241   } else {
242     while (*sep)
243       is_sep[(unsigned char) *sep++] = 1;
244   }
245   is_sep[0] = 1;                /* End of string is also separator */
246
247   /* Do the job */
248   p = q = s;
249   done = 0;
250
251   if (s[0] == '\0')
252     return res;
253
254   while (!done) {
255     char *topush;
256     while (!is_sep[(unsigned char) *q]) {
257       q++;
258     }
259     if (*q == '\0')
260       done = 1;
261
262     topush = xbt_malloc(q - p + 1);
263     memcpy(topush, p, q - p);
264     topush[q - p] = '\0';
265     xbt_dynar_push(res, &topush);
266     p = ++q;
267   }
268
269   return res;
270 }
271
272 /**
273  * \brief This functions splits a string after using another string as separator
274  * For example A!!B!!C splitted after !! will return the dynar {A,B,C}
275  * \return An array of dynars containing the string tokens
276  */
277 xbt_dynar_t xbt_str_split_str(const char *s, const char *sep)
278 {
279   xbt_dynar_t res = xbt_dynar_new(sizeof(char *), &xbt_free_ref);
280   int done;
281   const char *p, *q;
282
283   p = q = s;
284   done = 0;
285
286   if (s[0] == '\0')
287     return res;
288   if (sep[0] == '\0') {
289     s = xbt_strdup(s);
290     xbt_dynar_push(res, &s);
291     return res;
292   }
293
294   while (!done) {
295     char *to_push;
296     int v = 0;
297     //get the start of the first occurence of the substring
298     q = strstr(p, sep);
299     //if substring was not found add the entire string
300     if (NULL == q) {
301       v = strlen(p);
302       to_push = malloc(v + 1);
303       memcpy(to_push, p, v);
304       to_push[v] = '\0';
305       xbt_dynar_push(res, &to_push);
306       done = 1;
307     } else {
308       //get the appearance
309       to_push = malloc(q - p + 1);
310       memcpy(to_push, p, q - p);
311       //add string terminator
312       to_push[q - p] = '\0';
313       xbt_dynar_push(res, &to_push);
314       p = q + strlen(sep);
315     }
316   }
317   return res;
318 }
319
320 /** @brief Splits a string into a dynar of strings, taking quotes into account
321  *
322  * It basically does the same argument separation than the shell, where white
323  * spaces can be escaped and where arguments are never splitted within a
324  * quote group.
325  * Several subsequent spaces are ignored (unless within quotes, of course).
326  *
327  */
328
329 xbt_dynar_t xbt_str_split_quoted(const char *s)
330 {
331   xbt_dynar_t res = xbt_dynar_new(sizeof(char *), &xbt_free_ref);
332   char *str_to_free;            /* we have to copy the string before, to handle backslashes */
333   char *beg, *end;              /* pointers around the parsed chunk */
334   int in_simple_quote = 0, in_double_quote = 0;
335   int done = 0;
336   int ctn = 0;                  /* Got something in this block */
337
338   if (s[0] == '\0')
339     return res;
340   beg = str_to_free = xbt_strdup(s);
341
342   /* trim leading spaces */
343   xbt_str_ltrim(beg, " ");
344   end = beg;
345
346   while (!done) {
347
348
349     switch (*end) {
350     case '\\':
351       ctn = 1;
352       /* Protected char; move it closer */
353       memmove(end, end + 1, strlen(end));
354       if (*end == '\0')
355         THROW0(arg_error, 0, "String ends with \\");
356       end++;                    /* Pass the protected char */
357       break;
358
359     case '\'':
360       ctn = 1;
361       if (!in_double_quote) {
362         in_simple_quote = !in_simple_quote;
363         memmove(end, end + 1, strlen(end));
364       } else {
365         /* simple quote protected by double ones */
366         end++;
367       }
368       break;
369     case '"':
370       ctn = 1;
371       if (!in_simple_quote) {
372         in_double_quote = !in_double_quote;
373         memmove(end, end + 1, strlen(end));
374       } else {
375         /* double quote protected by simple ones */
376         end++;
377       }
378       break;
379
380     case ' ':
381     case '\t':
382     case '\n':
383     case '\0':
384       if (*end == '\0' && (in_simple_quote || in_double_quote)) {
385         THROW2(arg_error, 0,
386                "End of string found while searching for %c in %s",
387                (in_simple_quote ? '\'' : '"'), s);
388       }
389       if (in_simple_quote || in_double_quote) {
390         end++;
391       } else {
392         if (ctn) {
393           /* Found a separator. Push the string if contains something */
394           char *topush = xbt_malloc(end - beg + 1);
395           memcpy(topush, beg, end - beg);
396           topush[end - beg] = '\0';
397           xbt_dynar_push(res, &topush);
398         }
399         ctn = 0;
400
401         if (*end == '\0') {
402           done = 1;
403           break;
404         }
405
406         beg = ++end;
407         xbt_str_ltrim(beg, " ");
408         end = beg;
409       }
410       break;
411
412     default:
413       ctn = 1;
414       end++;
415     }
416   }
417   free(str_to_free);
418   xbt_dynar_shrink(res,0);
419   return res;
420 }
421
422 #ifdef SIMGRID_TEST
423 #include "xbt/str.h"
424
425 #define mytest(name, input, expected) \
426   xbt_test_add0(name); \
427   d=xbt_str_split_quoted(input); \
428   s=xbt_str_join(d,"XXX"); \
429   xbt_test_assert3(!strcmp(s,expected),\
430                    "Input (%s) leads to (%s) instead of (%s)", \
431                    input,s,expected);\
432                    free(s); \
433                    xbt_dynar_free(&d);
434
435 XBT_TEST_SUITE("xbt_str", "String Handling");
436 XBT_TEST_UNIT("xbt_str_split_quoted", test_split_quoted,"test the function xbt_str_split_quoted")
437 {
438   xbt_dynar_t d;
439   char *s;
440
441   mytest("Empty", "", "");
442   mytest("Basic test", "toto tutu", "totoXXXtutu");
443   mytest("Useless backslashes", "\\t\\o\\t\\o \\t\\u\\t\\u", "totoXXXtutu");
444   mytest("Protected space", "toto\\ tutu", "toto tutu");
445   mytest("Several spaces", "toto   tutu", "totoXXXtutu");
446   mytest("LTriming", "  toto tatu", "totoXXXtatu");
447   mytest("Triming", "  toto   tutu  ", "totoXXXtutu");
448   mytest("Single quotes", "'toto tutu' tata", "toto tutuXXXtata");
449   mytest("Double quotes", "\"toto tutu\" tata", "toto tutuXXXtata");
450   mytest("Mixed quotes", "\"toto' 'tutu\" tata", "toto' 'tutuXXXtata");
451   mytest("Backslashed quotes", "\\'toto tutu\\' tata",
452          "'totoXXXtutu'XXXtata");
453   mytest("Backslashed quotes + quotes", "'toto \\'tutu' tata",
454          "toto 'tutuXXXtata");
455
456 }
457
458 #define mytest_str(name, input, separator, expected) \
459   xbt_test_add0(name); \
460   d=xbt_str_split_str(input, separator); \
461   s=xbt_str_join(d,"XXX"); \
462   xbt_test_assert3(!strcmp(s,expected),\
463                    "Input (%s) leads to (%s) instead of (%s)", \
464                    input,s,expected);\
465                    free(s); \
466                    xbt_dynar_free(&d);
467
468 XBT_TEST_UNIT("xbt_str_split_str", test_split_str,"test the function xbt_str_split_str")
469 {
470   xbt_dynar_t d;
471   char *s;
472
473   mytest_str("Empty string and separator", "", "", "");
474   mytest_str("Empty string", "", "##", "");
475   mytest_str("Empty separator", "toto", "", "toto");
476   mytest_str("String with no separator in it", "toto", "##", "toto");
477   mytest_str("Basic test", "toto##tutu", "##", "totoXXXtutu");
478 }
479 #endif /* SIMGRID_TEST */
480
481 /** @brief Join a set of strings as a single string */
482
483 char *xbt_str_join(xbt_dynar_t dyn, const char *sep)
484 {
485   int len = 1, dyn_len = xbt_dynar_length(dyn);
486   unsigned int cpt;
487   char *cursor;
488   char *res, *p;
489
490   if (!dyn_len)
491     return xbt_strdup("");
492
493   /* compute the length */
494   xbt_dynar_foreach(dyn, cpt, cursor) {
495     len += strlen(cursor);
496   }
497   len += strlen(sep) * dyn_len;
498   /* Do the job */
499   res = xbt_malloc(len);
500   p = res;
501   xbt_dynar_foreach(dyn, cpt, cursor) {
502     if ((int) cpt < dyn_len - 1)
503       p += sprintf(p, "%s%s", cursor, sep);
504     else
505       p += sprintf(p, "%s", cursor);
506   }
507   return res;
508 }
509
510 #if defined(SIMGRID_NEED_GETLINE) || defined(DOXYGEN)
511 /** @brief Get a single line from the stream (reimplementation of the GNU getline)
512  *
513  * This is a redefinition of the GNU getline function, used on platforms where it does not exists.
514  *
515  * getline() reads an entire line from stream, storing the address of the buffer
516  * containing the text into *buf.  The buffer is null-terminated and includes
517  * the newline character, if one was found.
518  *
519  * If *buf is NULL, then getline() will allocate a buffer for storing the line,
520  * which should be freed by the user program.  Alternatively, before calling getline(),
521  * *buf can contain a pointer to a malloc()-allocated buffer *n bytes in size.  If the buffer
522  * is not large enough to hold the line, getline() resizes it with realloc(), updating *buf and *n
523  * as necessary.  In either case, on a successful call, *buf and *n will be updated to
524  * reflect the buffer address and allocated size respectively.
525  */
526 long getline(char **buf, size_t * n, FILE * stream)
527 {
528
529   size_t i;
530   int ch;
531
532   if (!*buf) {
533     *buf = xbt_malloc(512);
534     *n = 512;
535   }
536
537   if (feof(stream))
538     return (ssize_t) - 1;
539
540   for (i = 0; (ch = fgetc(stream)) != EOF; i++) {
541
542     if (i >= (*n) + 1)
543       *buf = xbt_realloc(*buf, *n += 512);
544
545     (*buf)[i] = ch;
546
547     if ((*buf)[i] == '\n') {
548       i++;
549       (*buf)[i] = '\0';
550       break;
551     }
552   }
553
554   if (i == *n)
555     *buf = xbt_realloc(*buf, *n += 1);
556
557   (*buf)[i] = '\0';
558
559   return (ssize_t) i;
560 }
561
562 #endif /* HAVE_GETLINE */
563
564 /*
565  * Diff related functions
566  */
567 static xbt_matrix_t diff_build_LCS(xbt_dynar_t da, xbt_dynar_t db)
568 {
569   xbt_matrix_t C = xbt_matrix_new(xbt_dynar_length(da), xbt_dynar_length(db),
570                                   sizeof(int), NULL);
571   unsigned long i, j;
572
573   /* Compute the LCS */
574   /*
575      C = array(0..m, 0..n)
576      for i := 0..m
577      C[i,0] = 0
578      for j := 1..n
579      C[0,j] = 0
580      for i := 1..m
581      for j := 1..n
582      if X[i] = Y[j]
583      C[i,j] := C[i-1,j-1] + 1
584      else:
585      C[i,j] := max(C[i,j-1], C[i-1,j])
586      return C[m,n]
587    */
588   if (xbt_dynar_length(db) != 0)
589     for (i = 0; i < xbt_dynar_length(da); i++)
590       *((int *) xbt_matrix_get_ptr(C, i, 0)) = 0;
591
592   if (xbt_dynar_length(da) != 0)
593     for (j = 0; j < xbt_dynar_length(db); j++)
594       *((int *) xbt_matrix_get_ptr(C, 0, j)) = 0;
595
596   for (i = 1; i < xbt_dynar_length(da); i++)
597     for (j = 1; j < xbt_dynar_length(db); j++) {
598
599       if (!strcmp
600           (xbt_dynar_get_as(da, i, char *), xbt_dynar_get_as(db, j, char *)))
601          *((int *) xbt_matrix_get_ptr(C, i, j)) =
602           xbt_matrix_get_as(C, i - 1, j - 1, int) + 1;
603       else
604         *((int *) xbt_matrix_get_ptr(C, i, j)) =
605           max(xbt_matrix_get_as(C, i, j - 1, int),
606               xbt_matrix_get_as(C, i - 1, j, int));
607     }
608   return C;
609 }
610
611 static void diff_build_diff(xbt_dynar_t res,
612                             xbt_matrix_t C,
613                             xbt_dynar_t da, xbt_dynar_t db, int i, int j)
614 {
615   char *topush;
616   /* Construct the diff
617      function printDiff(C[0..m,0..n], X[1..m], Y[1..n], i, j)
618      if i > 0 and j > 0 and X[i] = Y[j]
619      printDiff(C, X, Y, i-1, j-1)
620      print "  " + X[i]
621      else
622      if j > 0 and (i = 0 or C[i,j-1] >= C[i-1,j])
623      printDiff(C, X, Y, i, j-1)
624      print "+ " + Y[j]
625      else if i > 0 and (j = 0 or C[i,j-1] < C[i-1,j])
626      printDiff(C, X, Y, i-1, j)
627      print "- " + X[i]
628    */
629
630   if (i >= 0 && j >= 0 && !strcmp(xbt_dynar_get_as(da, i, char *),
631                                   xbt_dynar_get_as(db, j, char *))) {
632     diff_build_diff(res, C, da, db, i - 1, j - 1);
633     topush = bprintf("  %s", xbt_dynar_get_as(da, i, char *));
634     xbt_dynar_push(res, &topush);
635   } else if (j >= 0 &&
636              (i <= 0 || j == 0
637               || xbt_matrix_get_as(C, i, j - 1, int) >= xbt_matrix_get_as(C,
638                                                                           i -
639                                                                           1,
640                                                                           j,
641                                                                           int)))
642   {
643     diff_build_diff(res, C, da, db, i, j - 1);
644     topush = bprintf("+ %s", xbt_dynar_get_as(db, j, char *));
645     xbt_dynar_push(res, &topush);
646   } else if (i >= 0 &&
647              (j <= 0
648               || xbt_matrix_get_as(C, i, j - 1, int) < xbt_matrix_get_as(C,
649                                                                          i -
650                                                                          1, j,
651                                                                          int)))
652   {
653     diff_build_diff(res, C, da, db, i - 1, j);
654     topush = bprintf("- %s", xbt_dynar_get_as(da, i, char *));
655     xbt_dynar_push(res, &topush);
656   } else if (i <= 0 && j <= 0) {
657     return;
658   } else {
659     THROW2(arg_error, 0, "Invalid values: i=%d, j=%d", i, j);
660   }
661
662 }
663
664 /** @brief Compute the unified diff of two strings */
665 char *xbt_str_diff(char *a, char *b)
666 {
667   xbt_dynar_t da = xbt_str_split(a, "\n");
668   xbt_dynar_t db = xbt_str_split(b, "\n");
669
670   xbt_matrix_t C = diff_build_LCS(da, db);
671   xbt_dynar_t diff = xbt_dynar_new(sizeof(char *), &xbt_free_ref);
672   char *res = NULL;
673
674   diff_build_diff(diff, C, da, db, xbt_dynar_length(da) - 1,
675                   xbt_dynar_length(db) - 1);
676   /* Clean empty lines at the end */
677   while (xbt_dynar_length(diff) > 0) {
678     char *str;
679     xbt_dynar_pop(diff, &str);
680     if (str[0] == '\0' || !strcmp(str, "  ")) {
681       free(str);
682     } else {
683       xbt_dynar_push(diff, &str);
684       break;
685     }
686   }
687   res = xbt_str_join(diff, "\n");
688
689   xbt_dynar_free(&da);
690   xbt_dynar_free(&db);
691   xbt_dynar_free(&diff);
692   xbt_matrix_free(C);
693
694   return res;
695 }
696
697
698 /** @brief creates a new string containing what can be read on a fd
699  *
700  */
701 char* xbt_str_from_file(FILE *file) {
702   xbt_strbuff_t buff = xbt_strbuff_new();
703   char *res;
704   char bread[1024];
705   memset(bread,0,1024);
706
707   while (!feof(file)) {
708     int got = fread(bread, 1, 1023, file);
709     bread[got] = '\0';
710     xbt_strbuff_append(buff,bread);
711   }
712
713   res = buff->data;
714   xbt_strbuff_free_container(buff);
715   return res;
716 }