Merge remote-tracking branch 'origin/release-4-6' into HEAD
[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 std::string
135 replaceInternal(const std::string &input, const char *from, const char *to,
136                 bool bWholeWords)
137 {
138     GMX_RELEASE_ASSERT(from != NULL && to != NULL,
139                        "Replacement strings must not be NULL");
140     size_t matchLength = std::strlen(from);
141     std::string result;
142     size_t inputPos = 0;
143     size_t matchPos = input.find(from);
144     while (matchPos < input.length())
145     {
146         size_t matchEnd = matchPos + matchLength;
147         if (bWholeWords)
148         {
149             if (!((matchPos == 0 || !std::isalnum(input[matchPos-1]))
150                   && (matchEnd == input.length() || !std::isalnum(input[matchEnd]))))
151             {
152                 matchPos = input.find(from, matchPos + 1);
153                 continue;
154             }
155
156         }
157         result.append(input, inputPos, matchPos - inputPos);
158         result.append(to);
159         inputPos = matchEnd;
160         matchPos = input.find(from, inputPos);
161     }
162     result.append(input, inputPos, matchPos - inputPos);
163     return result;
164 }
165
166 } // namespace
167
168 std::string
169 replaceAll(const std::string &input, const char *from, const char *to)
170 {
171     return replaceInternal(input, from, to, false);
172 }
173
174 std::string
175 replaceAllWords(const std::string &input, const char *from, const char *to)
176 {
177     return replaceInternal(input, from, to, true);
178 }
179
180 /********************************************************************
181  * TextLineWrapper::Impl
182  */
183
184 /*! \internal \brief
185  * Private implementation class for TextLineWrapper.
186  *
187  * \ingroup module_utility
188  */
189 class TextLineWrapper::Impl
190 {
191     public:
192         //! Initialize default values for the wrapper.
193         Impl() : maxLength_(0) {}
194
195         /*! \brief
196          * Helper method to find the next wrapped line.
197          *
198          * \param[in]     input         Full input string.
199          * \param[in,out] lineStartPtr
200          *      Index of first character for the line to wrap.
201          *      On exit, index of the first character of the next line.
202          * \returns   Next output line.
203          */
204         std::string wrapNextLine(const std::string &input,
205                                  size_t *lineStartPtr) const;
206
207         //! Maximum length of output lines, or <= 0 if no limit.
208         int                     maxLength_;
209 };
210
211 std::string
212 TextLineWrapper::Impl::wrapNextLine(const std::string &input,
213                                     size_t *lineStartPtr) const
214 {
215     // Strip leading whitespace.
216     size_t lineStart = input.find_first_not_of(' ', *lineStartPtr);
217     if (lineStart == std::string::npos)
218     {
219         *lineStartPtr = lineStart;
220         return std::string();
221     }
222
223     size_t lineEnd = std::string::npos;
224     size_t nextNewline
225         = std::min(input.find('\n', lineStart), input.length());
226     if (maxLength_ <= 0 || nextNewline <= lineStart + maxLength_)
227     {
228         lineEnd = nextNewline;
229     }
230     else
231     {
232         size_t bestSpace = input.rfind(' ', lineStart + maxLength_);
233         if (bestSpace < lineStart || bestSpace == std::string::npos)
234         {
235             bestSpace = input.find(' ', lineStart);
236         }
237         lineEnd = std::min(bestSpace, nextNewline);
238     }
239
240     if (lineEnd == std::string::npos)
241     {
242         lineEnd = input.length();
243     }
244     *lineStartPtr = lineEnd + 1;
245     // Strip trailing whitespace.
246     while (lineEnd > lineStart && std::isspace(input[lineEnd - 1]))
247     {
248         --lineEnd;
249     }
250
251     size_t lineLength = lineEnd - lineStart;
252     return input.substr(lineStart, lineLength);
253 }
254
255 /********************************************************************
256  * TextLineWrapper
257  */
258
259 TextLineWrapper::TextLineWrapper()
260     : impl_(new Impl)
261 {
262 }
263
264 TextLineWrapper::~TextLineWrapper()
265 {
266 }
267
268 TextLineWrapper &TextLineWrapper::setLineLength(int length)
269 {
270     impl_->maxLength_ = length;
271     return *this;
272 }
273
274 std::string
275 TextLineWrapper::wrapToString(const std::string &input) const
276 {
277     std::string result;
278     size_t lineStart = 0;
279     size_t length = input.length();
280     while (lineStart < length)
281     {
282         result.append(impl_->wrapNextLine(input, &lineStart));
283         if (lineStart < length
284             || (lineStart == length && input[length - 1] == '\n'))
285         {
286             result.append("\n");
287         }
288     }
289     return result;
290 }
291
292 std::vector<std::string>
293 TextLineWrapper::wrapToVector(const std::string &input) const
294 {
295     std::vector<std::string> result;
296     size_t lineStart = 0;
297     while (lineStart < input.length())
298     {
299         result.push_back(impl_->wrapNextLine(input, &lineStart));
300     }
301     return result;
302 }
303
304 } // namespace gmx