Rename format.h to more generic stringutil.h.
[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 std::string formatString(const char *fmt, ...)
55 {
56     va_list ap;
57     char staticBuf[1024];
58     int length = 1024;
59     std::vector<char> dynamicBuf;
60     char *buf = staticBuf;
61
62     // TODO: There may be a better way of doing this on Windows, Microsoft
63     // provides their own way of doing things...
64     while (1)
65     {
66         va_start(ap, fmt);
67         int n = vsnprintf(buf, length, fmt, ap);
68         va_end(ap);
69         if (n > -1 && n < length)
70         {
71             std::string result(buf);
72             return result;
73         }
74         if (n > -1)
75         {
76             length = n + 1;
77         }
78         else
79         {
80             length *= 2;
81         }
82         dynamicBuf.resize(length);
83         buf = &dynamicBuf[0];
84     }
85 }
86
87 std::string concatenateStrings(const char *const *sarray, size_t count)
88 {
89     std::string result;
90
91     for (size_t i = 0; i < count && sarray[i] != NULL; ++i)
92     {
93         if (sarray[i][0] != '\0')
94         {
95             result.append(sarray[i]);
96             char lastchar = sarray[i][std::strlen(sarray[i])-1];
97             if (!std::isspace(lastchar))
98             {
99                 result.append(" ");
100             }
101         }
102     }
103     result.resize(result.find_last_not_of(" \n\r\t") + 1);
104     return result;
105 }
106
107 namespace
108 {
109
110 std::string
111 replaceInternal(const std::string &input, const char *from, const char *to,
112                 bool bWholeWords)
113 {
114     GMX_RELEASE_ASSERT(from != NULL && to != NULL,
115                        "Replacement strings must not be NULL");
116     size_t matchLength = std::strlen(from);
117     std::string result;
118     size_t inputPos = 0;
119     size_t matchPos = input.find(from);
120     while (matchPos < input.length())
121     {
122         size_t matchEnd = matchPos + matchLength;
123         if (bWholeWords)
124         {
125             if (!((matchPos == 0 || !std::isalnum(input[matchPos-1]))
126                   && (matchEnd == input.length() || !std::isalnum(input[matchEnd]))))
127             {
128                 matchPos = input.find(from, matchPos + 1);
129                 continue;
130             }
131
132         }
133         result.append(input, inputPos, matchPos - inputPos);
134         result.append(to);
135         inputPos = matchEnd;
136         matchPos = input.find(from, inputPos);
137     }
138     result.append(input, inputPos, matchPos - inputPos);
139     return result;
140 }
141
142 } // namespace
143
144 std::string
145 replaceAll(const std::string &input, const char *from, const char *to)
146 {
147     return replaceInternal(input, from, to, false);
148 }
149
150 std::string
151 replaceAllWords(const std::string &input, const char *from, const char *to)
152 {
153     return replaceInternal(input, from, to, true);
154 }
155
156 /********************************************************************
157  * TextLineWrapper::Impl
158  */
159
160 /*! \internal \brief
161  * Private implementation class for TextLineWrapper.
162  *
163  * \ingroup module_utility
164  */
165 class TextLineWrapper::Impl
166 {
167     public:
168         //! Initialize default values for the wrapper.
169         Impl() : maxLength_(0) {}
170
171         /*! \brief
172          * Helper method to find the next wrapped line.
173          *
174          * \param[in]     input         Full input string.
175          * \param[in,out] lineStartPtr
176          *      Index of first character for the line to wrap.
177          *      On exit, index of the first character of the next line.
178          * \returns   Next output line.
179          */
180         std::string wrapNextLine(const std::string &input,
181                                  size_t *lineStartPtr) const;
182
183         //! Maximum length of output lines, or <= 0 if no limit.
184         int                     maxLength_;
185 };
186
187 std::string
188 TextLineWrapper::Impl::wrapNextLine(const std::string &input,
189                                     size_t *lineStartPtr) const
190 {
191     // Strip leading whitespace.
192     size_t lineStart = input.find_first_not_of(' ', *lineStartPtr);
193     if (lineStart == std::string::npos)
194     {
195         *lineStartPtr = lineStart;
196         return std::string();
197     }
198
199     size_t lineEnd = std::string::npos;
200     size_t nextNewline
201         = std::min(input.find('\n', lineStart), input.length());
202     if (maxLength_ <= 0 || nextNewline <= lineStart + maxLength_)
203     {
204         lineEnd = nextNewline;
205     }
206     else
207     {
208         size_t bestSpace = input.rfind(' ', lineStart + maxLength_);
209         if (bestSpace < lineStart || bestSpace == std::string::npos)
210         {
211             bestSpace = input.find(' ', lineStart);
212         }
213         lineEnd = std::min(bestSpace, nextNewline);
214     }
215
216     if (lineEnd == std::string::npos)
217     {
218         lineEnd = input.length();
219     }
220     *lineStartPtr = lineEnd + 1;
221     // Strip trailing whitespace.
222     while (lineEnd > lineStart && std::isspace(input[lineEnd - 1]))
223     {
224         --lineEnd;
225     }
226
227     size_t lineLength = lineEnd - lineStart;
228     return input.substr(lineStart, lineLength);
229 }
230
231 /********************************************************************
232  * TextLineWrapper
233  */
234
235 TextLineWrapper::TextLineWrapper()
236     : impl_(new Impl)
237 {
238 }
239
240 TextLineWrapper::~TextLineWrapper()
241 {
242 }
243
244 TextLineWrapper &TextLineWrapper::setLineLength(int length)
245 {
246     impl_->maxLength_ = length;
247     return *this;
248 }
249
250 std::string
251 TextLineWrapper::wrapToString(const std::string &input) const
252 {
253     std::string result;
254     size_t lineStart = 0;
255     size_t length = input.length();
256     while (lineStart < length)
257     {
258         result.append(impl_->wrapNextLine(input, &lineStart));
259         if (lineStart < length
260             || (lineStart == length && input[length - 1] == '\n'))
261         {
262             result.append("\n");
263         }
264     }
265     return result;
266 }
267
268 std::vector<std::string>
269 TextLineWrapper::wrapToVector(const std::string &input) const
270 {
271     std::vector<std::string> result;
272     size_t lineStart = 0;
273     while (lineStart < input.length())
274     {
275         result.push_back(impl_->wrapNextLine(input, &lineStart));
276     }
277     return result;
278 }
279
280 } // namespace gmx