More features for TextLineWrapper.
[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 /********************************************************************
182  * TextLineWrapperSettings
183  */
184
185 TextLineWrapperSettings::TextLineWrapperSettings()
186     : maxLength_(0), indent_(0), firstLineIndent_(-1),
187       bStripLeadingWhitespace_(true), continuationChar_('\0')
188 {
189 }
190
191
192 /********************************************************************
193  * TextLineWrapper
194  */
195
196 size_t
197 TextLineWrapper::findNextLine(const char *input, size_t lineStart) const
198 {
199     size_t inputLength = std::strlen(input);
200     bool bFirstLine = (lineStart == 0 || input[lineStart - 1] == '\n');
201     // Ignore leading whitespace if necessary.
202     if (!bFirstLine || settings_.bStripLeadingWhitespace_)
203     {
204         lineStart += std::strspn(input + lineStart, " ");
205         if (lineStart >= inputLength)
206         {
207             return inputLength;
208         }
209     }
210
211     int indent = (bFirstLine ? settings_.firstLineIndent() : settings_.indent());
212     size_t lastAllowedBreakPoint
213         = (settings_.lineLength() > 0
214            ? std::min(lineStart + settings_.lineLength() - indent, inputLength)
215            : inputLength);
216     // Ignore trailing whitespace.
217     lastAllowedBreakPoint += std::strspn(input + lastAllowedBreakPoint, " ");
218     size_t lineEnd = lineStart;
219     do
220     {
221         const char *nextBreakPtr = std::strpbrk(input + lineEnd, " \n");
222         size_t nextBreak
223             = (nextBreakPtr != NULL ? nextBreakPtr - input : inputLength);
224         if (nextBreak > lastAllowedBreakPoint && lineEnd > lineStart)
225         {
226             break;
227         }
228         lineEnd = nextBreak + 1;
229     }
230     while (lineEnd < lastAllowedBreakPoint && input[lineEnd - 1] != '\n');
231     return (lineEnd < inputLength ? lineEnd : inputLength);
232 }
233
234 size_t
235 TextLineWrapper::findNextLine(const std::string &input, size_t lineStart) const
236 {
237     return findNextLine(input.c_str(), lineStart);
238 }
239
240 std::string
241 TextLineWrapper::formatLine(const std::string &input,
242                             size_t lineStart, size_t lineEnd) const
243 {
244     size_t inputLength = input.length();
245     bool bFirstLine = (lineStart == 0 || input[lineStart - 1] == '\n');
246     // Strip leading whitespace if necessary.
247     if (!bFirstLine || settings_.bStripLeadingWhitespace_)
248     {
249         lineStart = input.find_first_not_of(' ', lineStart);
250         if (lineStart >= inputLength)
251         {
252             return std::string();
253         }
254     }
255     int indent = (bFirstLine ? settings_.firstLineIndent() : settings_.indent());
256     bool bContinuation = (lineEnd < inputLength && input[lineEnd - 1] != '\n');
257     // Strip trailing whitespace.
258     while (lineEnd > lineStart && std::isspace(input[lineEnd - 1]))
259     {
260         --lineEnd;
261     }
262
263     size_t lineLength = lineEnd - lineStart;
264     std::string result(indent, ' ');
265     result.append(input, lineStart, lineLength);
266     if (bContinuation && settings_.continuationChar_ != '\0')
267     {
268         result.append(1, ' ');
269         result.append(1, settings_.continuationChar_);
270     }
271     return result;
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         size_t nextLineStart = findNextLine(input, lineStart);
283         result.append(formatLine(input, lineStart, nextLineStart));
284         if (nextLineStart < length
285             || (nextLineStart == length && input[length - 1] == '\n'))
286         {
287             result.append("\n");
288         }
289         lineStart = nextLineStart;
290     }
291     return result;
292 }
293
294 std::vector<std::string>
295 TextLineWrapper::wrapToVector(const std::string &input) const
296 {
297     std::vector<std::string> result;
298     size_t lineStart = 0;
299     size_t length = input.length();
300     while (lineStart < length)
301     {
302         size_t nextLineStart = findNextLine(input, lineStart);
303         result.push_back(formatLine(input, lineStart, nextLineStart));
304         lineStart = nextLineStart;
305     }
306     return result;
307 }
308
309 } // namespace gmx