Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / utility / stringutil.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements functions and classes in stringutil.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_utility
37  */
38 #include "stringutil.h"
39
40 #include <cctype>
41 #include <cstdio>
42 #include <cstdarg>
43 #include <cstring>
44
45 #include <algorithm>
46 #include <string>
47 #include <vector>
48
49 #include "gromacs/utility/gmxassert.h"
50
51 namespace gmx
52 {
53
54 bool endsWith(const std::string &str, const char *suffix)
55 {
56     if (suffix == NULL || suffix[0] == '\0')
57     {
58         return true;
59     }
60     size_t length = std::strlen(suffix);
61     return (str.length() >= length
62             && str.compare(str.length() - length, length, suffix) == 0);
63 }
64
65 std::string stripSuffixIfPresent(const std::string &str, const char *suffix)
66 {
67     if (suffix != NULL)
68     {
69         size_t suffixLength = std::strlen(suffix);
70         if (suffixLength > 0 && endsWith(str, suffix))
71         {
72             return str.substr(0, str.length() - suffixLength);
73         }
74     }
75     return str;
76 }
77
78 std::string formatString(const char *fmt, ...)
79 {
80     va_list ap;
81     char staticBuf[1024];
82     int length = 1024;
83     std::vector<char> dynamicBuf;
84     char *buf = staticBuf;
85
86     // TODO: There may be a better way of doing this on Windows, Microsoft
87     // provides their own way of doing things...
88     while (1)
89     {
90         va_start(ap, fmt);
91         int n = vsnprintf(buf, length, fmt, ap);
92         va_end(ap);
93         if (n > -1 && n < length)
94         {
95             std::string result(buf);
96             return result;
97         }
98         if (n > -1)
99         {
100             length = n + 1;
101         }
102         else
103         {
104             length *= 2;
105         }
106         dynamicBuf.resize(length);
107         buf = &dynamicBuf[0];
108     }
109 }
110
111 std::string concatenateStrings(const char *const *sarray, size_t count)
112 {
113     std::string result;
114
115     for (size_t i = 0; i < count && sarray[i] != NULL; ++i)
116     {
117         if (sarray[i][0] != '\0')
118         {
119             result.append(sarray[i]);
120             char lastchar = sarray[i][std::strlen(sarray[i])-1];
121             if (!std::isspace(lastchar))
122             {
123                 result.append(" ");
124             }
125         }
126     }
127     result.resize(result.find_last_not_of(" \n\r\t") + 1);
128     return result;
129 }
130
131 namespace
132 {
133
134 /*! \brief
135  * Common implementation for string replacement functions.
136  *
137  * \param[in] input  Input string.
138  * \param[in] from   String to find.
139  * \param[in] to     String to use to replace \p from.
140  * \param[in] bWholeWords  Whether to only consider matches to whole words.
141  * \returns   \p input with all occurrences of \p from replaced with \p to.
142  * \throws    std::bad_alloc if out of memory.
143  */
144 std::string
145 replaceInternal(const std::string &input, const char *from, const char *to,
146                 bool bWholeWords)
147 {
148     GMX_RELEASE_ASSERT(from != NULL && to != NULL,
149                        "Replacement strings must not be NULL");
150     size_t matchLength = std::strlen(from);
151     std::string result;
152     size_t inputPos = 0;
153     size_t matchPos = input.find(from);
154     while (matchPos < input.length())
155     {
156         size_t matchEnd = matchPos + matchLength;
157         if (bWholeWords)
158         {
159             if (!((matchPos == 0 || !std::isalnum(input[matchPos-1]))
160                   && (matchEnd == input.length() || !std::isalnum(input[matchEnd]))))
161             {
162                 matchPos = input.find(from, matchPos + 1);
163                 continue;
164             }
165
166         }
167         result.append(input, inputPos, matchPos - inputPos);
168         result.append(to);
169         inputPos = matchEnd;
170         matchPos = input.find(from, inputPos);
171     }
172     result.append(input, inputPos, matchPos - inputPos);
173     return result;
174 }
175
176 } // namespace
177
178 std::string
179 replaceAll(const std::string &input, const char *from, const char *to)
180 {
181     return replaceInternal(input, from, to, false);
182 }
183
184 std::string
185 replaceAllWords(const std::string &input, const char *from, const char *to)
186 {
187     return replaceInternal(input, from, to, true);
188 }
189
190
191 /********************************************************************
192  * TextLineWrapperSettings
193  */
194
195 TextLineWrapperSettings::TextLineWrapperSettings()
196     : maxLength_(0), indent_(0), firstLineIndent_(-1),
197       bStripLeadingWhitespace_(true), continuationChar_('\0')
198 {
199 }
200
201
202 /********************************************************************
203  * TextLineWrapper
204  */
205
206 size_t
207 TextLineWrapper::findNextLine(const char *input, size_t lineStart) const
208 {
209     size_t inputLength = std::strlen(input);
210     bool bFirstLine = (lineStart == 0 || input[lineStart - 1] == '\n');
211     // Ignore leading whitespace if necessary.
212     if (!bFirstLine || settings_.bStripLeadingWhitespace_)
213     {
214         lineStart += std::strspn(input + lineStart, " ");
215         if (lineStart >= inputLength)
216         {
217             return inputLength;
218         }
219     }
220
221     int indent = (bFirstLine ? settings_.firstLineIndent() : settings_.indent());
222     size_t lastAllowedBreakPoint
223         = (settings_.lineLength() > 0
224            ? std::min(lineStart + settings_.lineLength() - indent, inputLength)
225            : inputLength);
226     // Ignore trailing whitespace.
227     lastAllowedBreakPoint += std::strspn(input + lastAllowedBreakPoint, " ");
228     size_t lineEnd = lineStart;
229     do
230     {
231         const char *nextBreakPtr = std::strpbrk(input + lineEnd, " \n");
232         size_t nextBreak
233             = (nextBreakPtr != NULL ? nextBreakPtr - input : inputLength);
234         if (nextBreak > lastAllowedBreakPoint && lineEnd > lineStart)
235         {
236             break;
237         }
238         lineEnd = nextBreak + 1;
239     }
240     while (lineEnd < lastAllowedBreakPoint && input[lineEnd - 1] != '\n');
241     return (lineEnd < inputLength ? lineEnd : inputLength);
242 }
243
244 size_t
245 TextLineWrapper::findNextLine(const std::string &input, size_t lineStart) const
246 {
247     return findNextLine(input.c_str(), lineStart);
248 }
249
250 std::string
251 TextLineWrapper::formatLine(const std::string &input,
252                             size_t lineStart, size_t lineEnd) const
253 {
254     size_t inputLength = input.length();
255     bool bFirstLine = (lineStart == 0 || input[lineStart - 1] == '\n');
256     // Strip leading whitespace if necessary.
257     if (!bFirstLine || settings_.bStripLeadingWhitespace_)
258     {
259         lineStart = input.find_first_not_of(' ', lineStart);
260         if (lineStart >= inputLength)
261         {
262             return std::string();
263         }
264     }
265     int indent = (bFirstLine ? settings_.firstLineIndent() : settings_.indent());
266     bool bContinuation = (lineEnd < inputLength && input[lineEnd - 1] != '\n');
267     // Strip trailing whitespace.
268     while (lineEnd > lineStart && std::isspace(input[lineEnd - 1]))
269     {
270         --lineEnd;
271     }
272
273     size_t lineLength = lineEnd - lineStart;
274     std::string result(indent, ' ');
275     result.append(input, lineStart, lineLength);
276     if (bContinuation && settings_.continuationChar_ != '\0')
277     {
278         result.append(1, ' ');
279         result.append(1, settings_.continuationChar_);
280     }
281     return result;
282 }
283
284 std::string
285 TextLineWrapper::wrapToString(const std::string &input) const
286 {
287     std::string result;
288     size_t lineStart = 0;
289     size_t length = input.length();
290     while (lineStart < length)
291     {
292         size_t nextLineStart = findNextLine(input, lineStart);
293         result.append(formatLine(input, lineStart, nextLineStart));
294         if (nextLineStart < length
295             || (nextLineStart == length && input[length - 1] == '\n'))
296         {
297             result.append("\n");
298         }
299         lineStart = nextLineStart;
300     }
301     return result;
302 }
303
304 std::vector<std::string>
305 TextLineWrapper::wrapToVector(const std::string &input) const
306 {
307     std::vector<std::string> result;
308     size_t lineStart = 0;
309     size_t length = input.length();
310     while (lineStart < length)
311     {
312         size_t nextLineStart = findNextLine(input, lineStart);
313         result.push_back(formatLine(input, lineStart, nextLineStart));
314         lineStart = nextLineStart;
315     }
316     return result;
317 }
318
319 } // namespace gmx