Switch to unified view

a/src/utils/smallut.cpp b/src/utils/smallut.cpp
1
/* Copyright (C) 2004 J.F.Dockes
1
/* Copyright (C) 2004-2016 J.F.Dockes
2
 *   This program is free software; you can redistribute it and/or modify
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
5
 *   (at your option) any later version.
6
 *
6
 *
...
...
14
 *   Free Software Foundation, Inc.,
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
16
 */
17
17
18
#ifndef TEST_SMALLUT
18
#ifndef TEST_SMALLUT
19
#ifdef BUILDING_RECOLL
19
#include "autoconfig.h"
20
#include "autoconfig.h"
21
#else
22
#include "config.h"
23
#endif
20
24
21
#include <stdio.h>
25
#include <stdio.h>
22
#include <stdlib.h>
26
#include <stdlib.h>
23
#include <time.h>
27
#include <time.h>
24
#include <ctype.h>
28
#include <ctype.h>
...
...
32
#include <list>
36
#include <list>
33
#include UNORDERED_MAP_INCLUDE
37
#include UNORDERED_MAP_INCLUDE
34
#include UNORDERED_SET_INCLUDE
38
#include UNORDERED_SET_INCLUDE
35
39
36
#include "smallut.h"
40
#include "smallut.h"
37
#include "utf8iter.h"
38
#include "hldata.h"
39
#include "cstr.h"
40
41
41
using namespace std;
42
using namespace std;
42
43
43
void map_ss_cp_noshr(const map<string,string> s, map<string,string> *d)
44
int stringicmp(const string& s1, const string& s2)
44
{
45
    for (map<string,string>::const_iterator it= s.begin();
46
         it != s.end(); it++) {
47
        d->insert(
48
            pair<string,string>(string(it->first.begin(), it->first.end()),
49
                                string(it->second.begin(), it->second.end())));
50
    }
51
}
52
53
int stringicmp(const string & s1, const string& s2) 
54
{
45
{
55
    string::const_iterator it1 = s1.begin();
46
    string::const_iterator it1 = s1.begin();
56
    string::const_iterator it2 = s2.begin();
47
    string::const_iterator it2 = s2.begin();
57
    string::size_type size1 = s1.length(), size2 = s2.length();
48
    string::size_type size1 = s1.length(), size2 = s2.length();
58
    char c1, c2;
49
    char c1, c2;
59
50
60
    if (size1 < size2) {
51
    if (size1 < size2) {
61
  while (it1 != s1.end()) { 
52
        while (it1 != s1.end()) {
62
      c1 = ::toupper(*it1);
53
            c1 = ::toupper(*it1);
63
      c2 = ::toupper(*it2);
54
            c2 = ::toupper(*it2);
64
      if (c1 != c2) {
55
            if (c1 != c2) {
65
      return c1 > c2 ? 1 : -1;
56
                return c1 > c2 ? 1 : -1;
66
      }
57
            }
67
      ++it1; ++it2;
58
            ++it1;
68
  }
59
            ++it2;
60
        }
69
  return size1 == size2 ? 0 : -1;
61
        return size1 == size2 ? 0 : -1;
70
    } else {
62
    } else {
71
  while (it2 != s2.end()) { 
63
        while (it2 != s2.end()) {
72
      c1 = ::toupper(*it1);
64
            c1 = ::toupper(*it1);
73
      c2 = ::toupper(*it2);
65
            c2 = ::toupper(*it2);
74
      if (c1 != c2) {
66
            if (c1 != c2) {
75
      return c1 > c2 ? 1 : -1;
67
                return c1 > c2 ? 1 : -1;
76
      }
68
            }
77
      ++it1; ++it2;
69
            ++it1;
78
  }
70
            ++it2;
71
        }
79
  return size1 == size2 ? 0 : 1;
72
        return size1 == size2 ? 0 : 1;
80
    }
73
    }
81
}
74
}
82
void stringtolower(string& io)
75
void stringtolower(string& io)
83
{
76
{
84
    string::iterator it = io.begin();
77
    string::iterator it = io.begin();
85
    string::iterator ite = io.end();
78
    string::iterator ite = io.end();
86
    while (it != ite) {
79
    while (it != ite) {
87
  *it = ::tolower(*it);
80
        *it = ::tolower(*it);
88
  it++;
81
        it++;
89
    }
82
    }
90
}
83
}
91
string stringtolower(const string& i)
84
string stringtolower(const string& i)
92
{
85
{
93
    string o = i;
86
    string o = i;
...
...
95
    return o;
88
    return o;
96
}
89
}
97
extern int stringisuffcmp(const string& s1, const string& s2)
90
extern int stringisuffcmp(const string& s1, const string& s2)
98
{
91
{
99
    string::const_reverse_iterator r1 = s1.rbegin(), re1 = s1.rend(),
92
    string::const_reverse_iterator r1 = s1.rbegin(), re1 = s1.rend(),
100
  r2 = s2.rbegin(), re2 = s2.rend();
93
                                   r2 = s2.rbegin(), re2 = s2.rend();
101
    while (r1 != re1 && r2 != re2) {
94
    while (r1 != re1 && r2 != re2) {
102
  char c1 = ::toupper(*r1);
95
        char c1 = ::toupper(*r1);
103
  char c2 = ::toupper(*r2);
96
        char c2 = ::toupper(*r2);
104
  if (c1 != c2) {
97
        if (c1 != c2) {
105
      return c1 > c2 ? 1 : -1;
98
            return c1 > c2 ? 1 : -1;
106
  }
99
        }
107
  ++r1; ++r2;
100
        ++r1;
101
        ++r2;
108
    }
102
    }
109
    return 0;
103
    return 0;
110
}
104
}
111
105
112
//  s1 is already lowercase
106
//  s1 is already lowercase
113
int stringlowercmp(const string & s1, const string& s2) 
107
int stringlowercmp(const string& s1, const string& s2)
114
{
108
{
115
    string::const_iterator it1 = s1.begin();
109
    string::const_iterator it1 = s1.begin();
116
    string::const_iterator it2 = s2.begin();
110
    string::const_iterator it2 = s2.begin();
117
    string::size_type size1 = s1.length(), size2 = s2.length();
111
    string::size_type size1 = s1.length(), size2 = s2.length();
118
    char c2;
112
    char c2;
119
113
120
    if (size1 < size2) {
114
    if (size1 < size2) {
121
  while (it1 != s1.end()) { 
115
        while (it1 != s1.end()) {
122
      c2 = ::tolower(*it2);
116
            c2 = ::tolower(*it2);
123
      if (*it1 != c2) {
117
            if (*it1 != c2) {
124
      return *it1 > c2 ? 1 : -1;
118
                return *it1 > c2 ? 1 : -1;
125
      }
119
            }
126
      ++it1; ++it2;
120
            ++it1;
127
  }
121
            ++it2;
122
        }
128
  return size1 == size2 ? 0 : -1;
123
        return size1 == size2 ? 0 : -1;
129
    } else {
124
    } else {
130
  while (it2 != s2.end()) { 
125
        while (it2 != s2.end()) {
131
      c2 = ::tolower(*it2);
126
            c2 = ::tolower(*it2);
132
      if (*it1 != c2) {
127
            if (*it1 != c2) {
133
      return *it1 > c2 ? 1 : -1;
128
                return *it1 > c2 ? 1 : -1;
134
      }
129
            }
135
      ++it1; ++it2;
130
            ++it1;
136
  }
131
            ++it2;
132
        }
137
  return size1 == size2 ? 0 : 1;
133
        return size1 == size2 ? 0 : 1;
138
    }
134
    }
139
}
135
}
140
136
141
//  s1 is already uppercase
137
//  s1 is already uppercase
142
int stringuppercmp(const string & s1, const string& s2) 
138
int stringuppercmp(const string& s1, const string& s2)
143
{
139
{
144
    string::const_iterator it1 = s1.begin();
140
    string::const_iterator it1 = s1.begin();
145
    string::const_iterator it2 = s2.begin();
141
    string::const_iterator it2 = s2.begin();
146
    string::size_type size1 = s1.length(), size2 = s2.length();
142
    string::size_type size1 = s1.length(), size2 = s2.length();
147
    char c2;
143
    char c2;
148
144
149
    if (size1 < size2) {
145
    if (size1 < size2) {
150
  while (it1 != s1.end()) { 
146
        while (it1 != s1.end()) {
151
      c2 = ::toupper(*it2);
147
            c2 = ::toupper(*it2);
152
      if (*it1 != c2) {
148
            if (*it1 != c2) {
153
      return *it1 > c2 ? 1 : -1;
149
                return *it1 > c2 ? 1 : -1;
154
      }
150
            }
155
      ++it1; ++it2;
151
            ++it1;
156
  }
152
            ++it2;
153
        }
157
  return size1 == size2 ? 0 : -1;
154
        return size1 == size2 ? 0 : -1;
158
    } else {
155
    } else {
159
  while (it2 != s2.end()) { 
156
        while (it2 != s2.end()) {
160
      c2 = ::toupper(*it2);
157
            c2 = ::toupper(*it2);
161
      if (*it1 != c2) {
158
            if (*it1 != c2) {
162
      return *it1 > c2 ? 1 : -1;
159
                return *it1 > c2 ? 1 : -1;
163
      }
160
            }
164
      ++it1; ++it2;
161
            ++it1;
165
  }
162
            ++it2;
163
        }
166
  return size1 == size2 ? 0 : 1;
164
        return size1 == size2 ? 0 : 1;
167
    }
165
    }
168
}
166
}
169
167
170
// Compare charset names, removing the more common spelling variations
168
// Compare charset names, removing the more common spelling variations
171
bool samecharset(const string &cs1, const string &cs2)
169
bool samecharset(const string& cs1, const string& cs2)
172
{
170
{
173
    string mcs1, mcs2;
171
    string mcs1, mcs2;
174
    // Remove all - and _, turn to lowecase
172
    // Remove all - and _, turn to lowecase
175
    for (unsigned int i = 0; i < cs1.length();i++) {
173
    for (unsigned int i = 0; i < cs1.length(); i++) {
176
  if (cs1[i] != '_' && cs1[i] != '-') {
174
        if (cs1[i] != '_' && cs1[i] != '-') {
177
      mcs1 += ::tolower(cs1[i]);
175
            mcs1 += ::tolower(cs1[i]);
178
  }
179
    }
176
        }
177
    }
180
    for (unsigned int i = 0; i < cs2.length();i++) {
178
    for (unsigned int i = 0; i < cs2.length(); i++) {
181
  if (cs2[i] != '_' && cs2[i] != '-') {
179
        if (cs2[i] != '_' && cs2[i] != '-') {
182
      mcs2 += ::tolower(cs2[i]);
180
            mcs2 += ::tolower(cs2[i]);
183
  }
181
        }
184
    }
182
    }
185
    return mcs1 == mcs2;
183
    return mcs1 == mcs2;
186
}
184
}
187
185
188
template <class T> bool stringToStrings(const string &s, T &tokens, 
186
template <class T> bool stringToStrings(const string& s, T& tokens,
189
                                        const string& addseps)
187
                                        const string& addseps)
190
{
188
{
191
    string current;
189
    string current;
192
    tokens.clear();
190
    tokens.clear();
193
    enum states {SPACE, TOKEN, INQUOTE, ESCAPE};
191
    enum states {SPACE, TOKEN, INQUOTE, ESCAPE};
194
    states state = SPACE;
192
    states state = SPACE;
195
    for (unsigned int i = 0; i < s.length(); i++) {
193
    for (unsigned int i = 0; i < s.length(); i++) {
196
  switch (s[i]) {
194
        switch (s[i]) {
197
        case '"': 
195
        case '"':
198
      switch(state) {
196
            switch (state) {
199
            case SPACE: 
197
            case SPACE:
200
      state=INQUOTE; continue;
198
                state = INQUOTE;
199
                continue;
201
            case TOKEN: 
200
            case TOKEN:
202
          current += '"';
201
                current += '"';
203
      continue;
202
                continue;
204
            case INQUOTE: 
203
            case INQUOTE:
205
                tokens.insert(tokens.end(), current);
204
                tokens.insert(tokens.end(), current);
206
      current.clear();
205
                current.clear();
207
      state = SPACE;
206
                state = SPACE;
208
      continue;
207
                continue;
209
            case ESCAPE:
208
            case ESCAPE:
210
          current += '"';
209
                current += '"';
211
          state = INQUOTE;
210
                state = INQUOTE;
212
                continue;
211
                continue;
213
      }
212
            }
214
      break;
213
            break;
215
        case '\\': 
214
        case '\\':
216
      switch(state) {
215
            switch (state) {
217
            case SPACE: 
216
            case SPACE:
218
            case TOKEN: 
217
            case TOKEN:
219
                current += '\\';
218
                current += '\\';
220
                state=TOKEN; 
219
                state = TOKEN;
221
                continue;
220
                continue;
222
            case INQUOTE: 
221
            case INQUOTE:
223
                state = ESCAPE;
222
                state = ESCAPE;
224
                continue;
223
                continue;
225
            case ESCAPE:
224
            case ESCAPE:
226
                current += '\\';
225
                current += '\\';
227
                state = INQUOTE;
226
                state = INQUOTE;
228
                continue;
227
                continue;
229
      }
228
            }
230
      break;
229
            break;
231
230
232
        case ' ': 
231
        case ' ':
233
        case '\t': 
232
        case '\t':
234
        case '\n': 
233
        case '\n':
235
        case '\r': 
234
        case '\r':
236
      switch(state) {
235
            switch (state) {
237
            case SPACE: 
236
            case SPACE:
238
                continue;
237
                continue;
239
            case TOKEN: 
238
            case TOKEN:
240
      tokens.insert(tokens.end(), current);
239
                tokens.insert(tokens.end(), current);
241
      current.clear();
240
                current.clear();
242
      state = SPACE;
241
                state = SPACE;
243
      continue;
242
                continue;
244
            case INQUOTE: 
243
            case INQUOTE:
245
            case ESCAPE:
244
            case ESCAPE:
246
                current += s[i];
245
                current += s[i];
247
                continue;
246
                continue;
248
      }
247
            }
249
      break;
248
            break;
250
249
251
        default:
250
        default:
252
            if (!addseps.empty() && addseps.find(s[i]) != string::npos) {
251
            if (!addseps.empty() && addseps.find(s[i]) != string::npos) {
253
                switch(state) {
252
                switch (state) {
254
                case ESCAPE:
253
                case ESCAPE:
255
                    state = INQUOTE;
254
                    state = INQUOTE;
256
                    break;
255
                    break;
257
                case INQUOTE: 
256
                case INQUOTE:
258
                    break;
257
                    break;
259
                case SPACE: 
258
                case SPACE:
260
                    tokens.insert(tokens.end(), string(1, s[i]));
259
                    tokens.insert(tokens.end(), string(1, s[i]));
261
                    continue;
260
                    continue;
262
                case TOKEN: 
261
                case TOKEN:
263
                    tokens.insert(tokens.end(), current);
262
                    tokens.insert(tokens.end(), current);
264
                    current.erase();
263
                    current.erase();
265
                    tokens.insert(tokens.end(), string(1, s[i]));
264
                    tokens.insert(tokens.end(), string(1, s[i]));
266
                    state = SPACE;
265
                    state = SPACE;
267
                    continue;
266
                    continue;
268
                }
267
                }
269
            } else switch(state) {
268
            } else switch (state) {
270
                case ESCAPE:
269
                case ESCAPE:
271
                    state = INQUOTE;
270
                    state = INQUOTE;
272
                    break;
271
                    break;
273
                case SPACE: 
272
                case SPACE:
274
                    state = TOKEN;
273
                    state = TOKEN;
275
                    break;
274
                    break;
276
                case TOKEN: 
275
                case TOKEN:
277
                case INQUOTE: 
276
                case INQUOTE:
278
                    break;
277
                    break;
279
                }
278
                }
280
      current += s[i];
279
            current += s[i];
281
  }
282
    }
280
        }
281
    }
283
    switch(state) {
282
    switch (state) {
284
    case SPACE: 
283
    case SPACE:
285
  break;
284
        break;
286
    case TOKEN: 
285
    case TOKEN:
287
  tokens.insert(tokens.end(), current);
286
        tokens.insert(tokens.end(), current);
288
  break;
287
        break;
289
    case INQUOTE: 
288
    case INQUOTE:
290
    case ESCAPE:
289
    case ESCAPE:
291
  return false;
290
        return false;
292
    }
291
    }
293
    return true;
292
    return true;
294
}
293
}
295
294
296
template bool stringToStrings<list<string> >(const string &, 
295
template bool stringToStrings<list<string> >(const string&,
297
                       list<string> &, const string&);
296
        list<string>&, const string&);
298
template bool stringToStrings<vector<string> >(const string &, 
297
template bool stringToStrings<vector<string> >(const string&,
299
                         vector<string> &,const string&);
298
        vector<string>&, const string&);
300
template bool stringToStrings<set<string> >(const string &,
299
template bool stringToStrings<set<string> >(const string&,
301
                      set<string> &, const string&);
300
        set<string>&, const string&);
302
template bool stringToStrings<STD_UNORDERED_SET<string> >
301
template bool stringToStrings<STD_UNORDERED_SET<string> >
303
(const string &, STD_UNORDERED_SET<string> &, const string&);
302
(const string&, STD_UNORDERED_SET<string>&, const string&);
304
303
305
template <class T> void stringsToString(const T &tokens, string &s) 
304
template <class T> void stringsToString(const T& tokens, string& s)
306
{
305
{
307
    for (typename T::const_iterator it = tokens.begin();
306
    for (typename T::const_iterator it = tokens.begin();
308
   it != tokens.end(); it++) {
307
            it != tokens.end(); it++) {
309
  bool hasblanks = false;
308
        bool hasblanks = false;
310
  if (it->find_first_of(" \t\n") != string::npos)
309
        if (it->find_first_of(" \t\n") != string::npos) {
311
      hasblanks = true;
310
            hasblanks = true;
311
        }
312
  if (it != tokens.begin())
312
        if (it != tokens.begin()) {
313
      s.append(1, ' ');
313
            s.append(1, ' ');
314
  if (hasblanks)
314
        }
315
        if (hasblanks) {
315
      s.append(1, '"');
316
            s.append(1, '"');
317
        }
316
  for (unsigned int i = 0; i < it->length(); i++) {
318
        for (unsigned int i = 0; i < it->length(); i++) {
317
      char car = it->at(i);
319
            char car = it->at(i);
318
      if (car == '"') {
320
            if (car == '"') {
319
      s.append(1, '\\');
321
                s.append(1, '\\');
320
      s.append(1, car);
322
                s.append(1, car);
321
      } else {
323
            } else {
322
      s.append(1, car);
324
                s.append(1, car);
325
            }
326
        }
327
        if (hasblanks) {
328
            s.append(1, '"');
329
        }
323
      }
330
    }
324
  }
331
}
325
  if (hasblanks)
326
      s.append(1, '"');
327
    }
328
}
329
template void stringsToString<list<string> >(const list<string> &, string &);
332
template void stringsToString<list<string> >(const list<string>&, string&);
330
template void stringsToString<vector<string> >(const vector<string> &,string &);
333
template void stringsToString<vector<string> >(const vector<string>&, string&);
331
template void stringsToString<set<string> >(const set<string> &, string &);
334
template void stringsToString<set<string> >(const set<string>&, string&);
332
template <class T> string stringsToString(const T &tokens)
335
template <class T> string stringsToString(const T& tokens)
333
{
336
{
334
    string out;
337
    string out;
335
    stringsToString<T>(tokens, out);
338
    stringsToString<T>(tokens, out);
336
    return out;
339
    return out;
337
}
340
}
338
template string stringsToString<list<string> >(const list<string> &);
341
template string stringsToString<list<string> >(const list<string>&);
339
template string stringsToString<vector<string> >(const vector<string> &);
342
template string stringsToString<vector<string> >(const vector<string>&);
340
template string stringsToString<set<string> >(const set<string> &);
343
template string stringsToString<set<string> >(const set<string>&);
341
344
342
template <class T> void stringsToCSV(const T &tokens, string &s, 
345
template <class T> void stringsToCSV(const T& tokens, string& s,
343
                   char sep)
346
                                     char sep)
344
{
347
{
345
    s.erase();
348
    s.erase();
346
    for (typename T::const_iterator it = tokens.begin();
349
    for (typename T::const_iterator it = tokens.begin();
347
   it != tokens.end(); it++) {
350
            it != tokens.end(); it++) {
348
  bool needquotes = false;
351
        bool needquotes = false;
349
  if (it->empty() || 
352
        if (it->empty() ||
350
      it->find_first_of(string(1, sep) + "\"\n") != string::npos)
353
                it->find_first_of(string(1, sep) + "\"\n") != string::npos) {
351
      needquotes = true;
354
            needquotes = true;
355
        }
352
  if (it != tokens.begin())
356
        if (it != tokens.begin()) {
353
      s.append(1, sep);
357
            s.append(1, sep);
354
  if (needquotes)
358
        }
359
        if (needquotes) {
355
      s.append(1, '"');
360
            s.append(1, '"');
361
        }
356
  for (unsigned int i = 0; i < it->length(); i++) {
362
        for (unsigned int i = 0; i < it->length(); i++) {
357
      char car = it->at(i);
363
            char car = it->at(i);
358
      if (car == '"') {
364
            if (car == '"') {
359
      s.append(2, '"');
365
                s.append(2, '"');
360
      } else {
366
            } else {
361
      s.append(1, car);
367
                s.append(1, car);
368
            }
369
        }
370
        if (needquotes) {
371
            s.append(1, '"');
372
        }
362
      }
373
    }
363
  }
374
}
364
  if (needquotes)
365
      s.append(1, '"');
366
    }
367
}
368
template void stringsToCSV<list<string> >(const list<string> &, string &, char);
375
template void stringsToCSV<list<string> >(const list<string>&, string&, char);
369
template void stringsToCSV<vector<string> >(const vector<string> &,string &, 
376
template void stringsToCSV<vector<string> >(const vector<string>&, string&,
370
                      char);
377
        char);
371
378
372
void stringToTokens(const string& str, vector<string>& tokens,
379
void stringToTokens(const string& str, vector<string>& tokens,
373
          const string& delims, bool skipinit)
380
                    const string& delims, bool skipinit)
374
{
381
{
375
    string::size_type startPos = 0, pos;
382
    string::size_type startPos = 0, pos;
376
383
377
    // Skip initial delims, return empty if this eats all.
384
    // Skip initial delims, return empty if this eats all.
378
    if (skipinit && 
385
    if (skipinit &&
379
  (startPos = str.find_first_not_of(delims, 0)) == string::npos) {
386
            (startPos = str.find_first_not_of(delims, 0)) == string::npos) {
380
  return;
387
        return;
381
    }
388
    }
382
    while (startPos < str.size()) { 
389
    while (startPos < str.size()) {
383
        // Find next delimiter or end of string (end of token)
390
        // Find next delimiter or end of string (end of token)
384
        pos = str.find_first_of(delims, startPos);
391
        pos = str.find_first_of(delims, startPos);
385
392
386
        // Add token to the vector and adjust start
393
        // Add token to the vector and adjust start
387
  if (pos == string::npos) {
394
        if (pos == string::npos) {
388
      tokens.push_back(str.substr(startPos));
395
            tokens.push_back(str.substr(startPos));
389
      break;
396
            break;
390
  } else if (pos == startPos) {
397
        } else if (pos == startPos) {
391
      // Dont' push empty tokens after first
398
            // Dont' push empty tokens after first
392
      if (tokens.empty())
399
            if (tokens.empty()) {
393
      tokens.push_back(string());
400
                tokens.push_back(string());
401
            }
394
      startPos = ++pos;
402
            startPos = ++pos;
395
  } else {
403
        } else {
396
      tokens.push_back(str.substr(startPos, pos - startPos));
404
            tokens.push_back(str.substr(startPos, pos - startPos));
397
      startPos = ++pos;
405
            startPos = ++pos;
406
        }
407
    }
398
  }
408
}
399
    }
400
}
401
409
402
bool stringToBool(const string &s)
410
bool stringToBool(const string& s)
403
{
411
{
404
    if (s.empty())
412
    if (s.empty()) {
405
  return false;
413
        return false;
414
    }
406
    if (isdigit(s[0])) {
415
    if (isdigit(s[0])) {
407
  int val = atoi(s.c_str());
416
        int val = atoi(s.c_str());
408
  return val ? true : false;
417
        return val ? true : false;
409
    }
418
    }
410
    if (s.find_first_of("yYtT") == 0)
419
    if (s.find_first_of("yYtT") == 0) {
411
  return true;
420
        return true;
421
    }
412
    return false;
422
    return false;
413
}
423
}
414
424
415
void trimstring(string &s, const char *ws)
425
void trimstring(string& s, const char *ws)
416
{
426
{
417
    string::size_type pos = s.find_first_not_of(ws);
427
    string::size_type pos = s.find_first_not_of(ws);
418
    if (pos == string::npos) {
428
    if (pos == string::npos) {
419
  s.clear();
429
        s.clear();
420
  return;
430
        return;
421
    }
431
    }
422
    s.replace(0, pos, string());
432
    s.replace(0, pos, string());
423
433
424
    pos = s.find_last_not_of(ws);
434
    pos = s.find_last_not_of(ws);
425
    if (pos != string::npos && pos != s.length()-1)
435
    if (pos != string::npos && pos != s.length() - 1) {
426
  s.replace(pos+1, string::npos, string());
436
        s.replace(pos + 1, string::npos, string());
437
    }
427
}
438
}
428
439
429
// Remove some chars and replace them with spaces
440
// Remove some chars and replace them with spaces
430
string neutchars(const string &str, const string &chars)
441
string neutchars(const string& str, const string& chars)
431
{
442
{
432
    string out;
443
    string out;
433
    neutchars(str, out, chars);
444
    neutchars(str, out, chars);
434
    return out;
445
    return out;
435
}
446
}
436
void neutchars(const string &str, string &out, const string& chars)
447
void neutchars(const string& str, string& out, const string& chars)
437
{
448
{
438
    string::size_type startPos, pos;
449
    string::size_type startPos, pos;
439
450
440
    for (pos = 0;;) { 
451
    for (pos = 0;;) {
441
        // Skip initial chars, break if this eats all.
452
        // Skip initial chars, break if this eats all.
442
        if ((startPos = str.find_first_not_of(chars, pos)) == string::npos)
453
        if ((startPos = str.find_first_not_of(chars, pos)) == string::npos) {
443
      break;
454
            break;
455
        }
444
        // Find next delimiter or end of string (end of token)
456
        // Find next delimiter or end of string (end of token)
445
        pos = str.find_first_of(chars, startPos);
457
        pos = str.find_first_of(chars, startPos);
446
        // Add token to the output. Note: token cant be empty here
458
        // Add token to the output. Note: token cant be empty here
447
  if (pos == string::npos) {
459
        if (pos == string::npos) {
448
      out += str.substr(startPos);
460
            out += str.substr(startPos);
449
  } else {
461
        } else {
450
      out += str.substr(startPos, pos - startPos) + " ";
462
            out += str.substr(startPos, pos - startPos) + " ";
451
  }
463
        }
452
    }
464
    }
453
}
465
}
454
466
455
467
456
/* Truncate a string to a given maxlength, avoiding cutting off midword
468
/* Truncate a string to a given maxlength, avoiding cutting off midword
457
 * if reasonably possible. Note: we could also use textsplit, stopping when
469
 * if reasonably possible. Note: we could also use textsplit, stopping when
458
 * we have enough, this would be cleanly utf8-aware but would remove 
470
 * we have enough, this would be cleanly utf8-aware but would remove
459
 * punctuation */
471
 * punctuation */
460
static const string cstr_SEPAR = " \t\n\r-:.;,/[]{}";
472
static const string cstr_SEPAR = " \t\n\r-:.;,/[]{}";
461
string truncate_to_word(const string &input, string::size_type maxlen)
473
string truncate_to_word(const string& input, string::size_type maxlen)
462
{
474
{
463
    string output;
475
    string output;
464
    if (input.length() <= maxlen) {
476
    if (input.length() <= maxlen) {
465
  output = input;
477
        output = input;
466
    } else {
478
    } else {
467
  output = input.substr(0, maxlen);
479
        output = input.substr(0, maxlen);
468
  string::size_type space = output.find_last_of(cstr_SEPAR);
480
        string::size_type space = output.find_last_of(cstr_SEPAR);
469
  // Original version only truncated at space if space was found after
481
        // Original version only truncated at space if space was found after
470
  // maxlen/2. But we HAVE to truncate at space, else we'd need to do
482
        // maxlen/2. But we HAVE to truncate at space, else we'd need to do
471
  // utf8 stuff to avoid truncating at multibyte char. In any case,
483
        // utf8 stuff to avoid truncating at multibyte char. In any case,
472
  // not finding space means that the text probably has no value.
484
        // not finding space means that the text probably has no value.
473
  // Except probably for Asian languages, so we may want to fix this 
485
        // Except probably for Asian languages, so we may want to fix this
474
  // one day
486
        // one day
475
  if (space == string::npos) {
487
        if (space == string::npos) {
476
      output.erase();
488
            output.erase();
477
  } else {
489
        } else {
478
      output.erase(space);
490
            output.erase(space);
479
  }
491
        }
480
    }
492
    }
481
    return output;
493
    return output;
482
}
494
}
483
495
484
void utf8truncate(string &s, int maxlen)
485
{
486
    if (s.size() <= string::size_type(maxlen))
487
  return;
488
    Utf8Iter iter(s);
489
    string::size_type pos = 0;
490
    while (iter++ != string::npos) 
491
  if (iter.getBpos() < string::size_type(maxlen))
492
      pos = iter.getBpos();
493
494
    s.erase(pos);
495
}
496
497
// Escape things that would look like markup
496
// Escape things that would look like markup
498
string escapeHtml(const string &in)
497
string escapeHtml(const string& in)
499
{
498
{
500
    string out;
499
    string out;
501
    for (string::size_type pos = 0; pos < in.length(); pos++) {
500
    for (string::size_type pos = 0; pos < in.length(); pos++) {
502
  switch(in.at(pos)) {
501
        switch (in.at(pos)) {
503
  case '<':
502
        case '<':
504
      out += "&lt;";
503
            out += "&lt;";
505
      break;
504
            break;
506
  case '&':
505
        case '&':
507
      out += "&amp;";
506
            out += "&amp;";
508
      break;
507
            break;
509
  default:
508
        default:
510
      out += in.at(pos);
509
            out += in.at(pos);
511
  }
510
        }
512
    }
511
    }
513
    return out;
512
    return out;
514
}
513
}
515
514
516
string escapeShell(const string &in)
515
string escapeShell(const string& in)
517
{
516
{
518
    string out;
517
    string out;
519
    out += "\"";
518
    out += "\"";
520
    for (string::size_type pos = 0; pos < in.length(); pos++) {
519
    for (string::size_type pos = 0; pos < in.length(); pos++) {
521
  switch(in.at(pos)) {
520
        switch (in.at(pos)) {
522
  case '$':
521
        case '$':
523
      out += "\\$";
522
            out += "\\$";
524
      break;
523
            break;
525
  case '`':
524
        case '`':
526
      out += "\\`";
525
            out += "\\`";
527
      break;
526
            break;
528
  case '"':
527
        case '"':
529
      out += "\\\"";
528
            out += "\\\"";
530
      break;
529
            break;
531
  case '\n':
530
        case '\n':
532
      out += "\\\n";
531
            out += "\\\n";
533
      break;
532
            break;
534
  case '\\':
533
        case '\\':
535
      out += "\\\\";
534
            out += "\\\\";
536
      break;
535
            break;
537
  default:
536
        default:
538
      out += in.at(pos);
537
            out += in.at(pos);
539
  }
538
        }
540
    }
539
    }
541
    out += "\"";
540
    out += "\"";
542
    return out;
541
    return out;
543
}
542
}
544
543
545
544
546
// Substitute printf-like percent cmds inside a string
545
// Substitute printf-like percent cmds inside a string
547
bool pcSubst(const string& in, string& out, const map<char, string>& subs)
546
bool pcSubst(const string& in, string& out, const map<char, string>& subs)
548
{
547
{
549
    string::const_iterator it;
548
    string::const_iterator it;
550
    for (it = in.begin(); it != in.end();it++) {
549
    for (it = in.begin(); it != in.end(); it++) {
551
  if (*it == '%') {
552
      if (++it == in.end()) {
553
      out += '%';
554
      break;
555
      }
556
      if (*it == '%') {
550
        if (*it == '%') {
557
      out += '%';
551
            if (++it == in.end()) {
558
      continue;
552
                out += '%';
559
      }
553
                break;
554
            }
555
            if (*it == '%') {
556
                out += '%';
557
                continue;
558
            }
560
      map<char,string>::const_iterator tr;
559
            map<char, string>::const_iterator tr;
561
      if ((tr = subs.find(*it)) != subs.end()) {
560
            if ((tr = subs.find(*it)) != subs.end()) {
562
      out += tr->second;
561
                out += tr->second;
563
      } else {
562
            } else {
564
      // We used to do "out += *it;" here but this does not make
563
                // We used to do "out += *it;" here but this does not make
565
                // sense
564
                // sense
566
      }
565
            }
567
  } else {
566
        } else {
568
      out += *it;
567
            out += *it;
569
  }
568
        }
570
    }
569
    }
571
    return true;
570
    return true;
572
}
571
}
573
572
574
bool pcSubst(const string& in, string& out, const map<string, string>& subs)
573
bool pcSubst(const string& in, string& out, const map<string, string>& subs)
575
{
574
{
576
    out.erase();
575
    out.erase();
577
    string::size_type i;
576
    string::size_type i;
578
    for (i = 0; i < in.size(); i++) {
577
    for (i = 0; i < in.size(); i++) {
579
  if (in[i] == '%') {
580
      if (++i == in.size()) {
581
      out += '%';
582
      break;
583
      }
584
      if (in[i] == '%') {
578
        if (in[i] == '%') {
585
      out += '%';
579
            if (++i == in.size()) {
586
      continue;
580
                out += '%';
587
      }
581
                break;
582
            }
583
            if (in[i] == '%') {
584
                out += '%';
585
                continue;
586
            }
588
            string key = "";
587
            string key = "";
589
            if (in[i] == '(') {
588
            if (in[i] == '(') {
590
                if (++i == in.size()) {
589
                if (++i == in.size()) {
591
                    out += string("%(");
590
                    out += string("%(");
592
                    break;
591
                    break;
593
                }
592
                }
594
                string::size_type j = in.find_first_of(")", i);
593
                string::size_type j = in.find_first_of(")", i);
595
                if (j == string::npos) {
594
                if (j == string::npos) {
596
                    // ??concatenate remaining part and stop
595
                    // ??concatenate remaining part and stop
597
                    out += in.substr(i-2);
596
                    out += in.substr(i - 2);
598
                    break;
597
                    break;
599
                }
598
                }
600
                key = in.substr(i, j-i);
599
                key = in.substr(i, j - i);
601
                i = j;
600
                i = j;
602
            } else {
601
            } else {
603
                key = in[i];
602
                key = in[i];
604
            }
603
            }
605
      map<string,string>::const_iterator tr;
604
            map<string, string>::const_iterator tr;
606
      if ((tr = subs.find(key)) != subs.end()) {
605
            if ((tr = subs.find(key)) != subs.end()) {
607
      out += tr->second;
606
                out += tr->second;
608
      } else {
607
            } else {
609
                // Substitute to nothing, that's the reasonable thing to do
608
                // Substitute to nothing, that's the reasonable thing to do
610
                // instead of keeping the %(key)
609
                // instead of keeping the %(key)
611
                // out += key.size()==1? key : string("(") + key + string(")");
610
                // out += key.size()==1? key : string("(") + key + string(")");
612
      }
611
            }
613
  } else {
612
        } else {
614
      out += in[i];
613
            out += in[i];
615
  }
614
        }
616
    }
615
    }
617
    return true;
616
    return true;
618
}
617
}
619
inline static int ulltorbuf(unsigned long long val, char *rbuf)
618
inline static int ulltorbuf(unsigned long long val, char *rbuf)
620
{
619
{
621
    int idx;
620
    int idx;
622
    for (idx = 0; val; idx++) {
621
    for (idx = 0; val; idx++) {
623
        rbuf[idx] = '0' + val % 10;
622
        rbuf[idx] = '0' + val % 10;
624
        val /= 10;
623
        val /= 10;
624
    }
625
    } while (val);
625
    while (val);
626
    rbuf[idx] = 0;
626
    rbuf[idx] = 0;
627
    return idx;
627
    return idx;
628
}
628
}
629
629
630
inline static void ullcopyreverse(const char *rbuf, string& buf, int idx)
630
inline static void ullcopyreverse(const char *rbuf, string& buf, int idx)
631
{
631
{
632
    buf.reserve(idx+1);
632
    buf.reserve(idx + 1);
633
    for (int i = idx - 1; i >= 0; i--) {
633
    for (int i = idx - 1; i >= 0; i--) {
634
        buf.push_back(rbuf[i]);
634
        buf.push_back(rbuf[i]);
635
    }
635
    }
636
}
636
}
637
637
...
...
657
        buf = "0";
657
        buf = "0";
658
        return;
658
        return;
659
    }
659
    }
660
660
661
    bool neg = val < 0;
661
    bool neg = val < 0;
662
    if (neg)
662
    if (neg) {
663
        val = -val;
663
        val = -val;
664
    }
664
665
665
    char rbuf[30];
666
    char rbuf[30];
666
    int idx = ulltorbuf(val, rbuf);
667
    int idx = ulltorbuf(val, rbuf);
667
668
668
    if (neg)
669
    if (neg) {
669
        rbuf[idx++] = '-';
670
        rbuf[idx++] = '-';
671
    }
670
    rbuf[idx] = 0;
672
    rbuf[idx] = 0;
671
673
672
    ullcopyreverse(rbuf, buf, idx);
674
    ullcopyreverse(rbuf, buf, idx);
673
    return;
675
    return;
674
}
676
}
...
...
689
691
690
// Convert byte count into unit (KB/MB...) appropriate for display
692
// Convert byte count into unit (KB/MB...) appropriate for display
691
string displayableBytes(off_t size)
693
string displayableBytes(off_t size)
692
{
694
{
693
    const char *unit;
695
    const char *unit;
694
    
696
695
    double roundable = 0;
697
    double roundable = 0;
696
    if (size < 1000) {
698
    if (size < 1000) {
697
  unit = " B ";
699
        unit = " B ";
698
  roundable = double(size);
700
        roundable = double(size);
699
    } else if (size < 1E6) {
701
    } else if (size < 1E6) {
700
  unit = " KB ";
702
        unit = " KB ";
701
  roundable = double(size) / 1E3;
703
        roundable = double(size) / 1E3;
702
    } else if (size < 1E9) {
704
    } else if (size < 1E9) {
703
  unit = " MB ";
705
        unit = " MB ";
704
  roundable = double(size) / 1E6;
706
        roundable = double(size) / 1E6;
705
    } else {
707
    } else {
706
  unit = " GB ";
708
        unit = " GB ";
707
  roundable = double(size) / 1E9;
709
        roundable = double(size) / 1E9;
708
    }
710
    }
709
    size = off_t(round(roundable));
711
    size = off_t(round(roundable));
710
    return lltodecstr(size).append(unit);
712
    return lltodecstr(size).append(unit);
711
}
713
}
712
714
713
string breakIntoLines(const string& in, unsigned int ll, 
715
string breakIntoLines(const string& in, unsigned int ll,
714
            unsigned int maxlines)
716
                      unsigned int maxlines)
715
{
717
{
716
    string query = in;
718
    string query = in;
717
    string oq;
719
    string oq;
718
    unsigned int nlines = 0;
720
    unsigned int nlines = 0;
719
    while (query.length() > 0) {
721
    while (query.length() > 0) {
720
  string ss = query.substr(0, ll);
722
        string ss = query.substr(0, ll);
721
  if (ss.length() == ll) {
723
        if (ss.length() == ll) {
722
      string::size_type pos = ss.find_last_of(" ");
724
            string::size_type pos = ss.find_last_of(" ");
723
      if (pos == string::npos) {
725
            if (pos == string::npos) {
724
      pos = query.find_first_of(" ");
726
                pos = query.find_first_of(" ");
725
      if (pos != string::npos)
727
                if (pos != string::npos) {
726
          ss = query.substr(0, pos+1);
728
                    ss = query.substr(0, pos + 1);
727
      else 
729
                } else {
728
          ss = query;
730
                    ss = query;
729
      } else {
731
                }
730
      ss = ss.substr(0, pos+1);
732
            } else {
731
      }
733
                ss = ss.substr(0, pos + 1);
732
  }
734
            }
735
        }
733
  // This cant happen, but anyway. Be very sure to avoid an infinite loop
736
        // This cant happen, but anyway. Be very sure to avoid an infinite loop
734
  if (ss.length() == 0) {
737
        if (ss.length() == 0) {
735
      oq = query;
738
            oq = query;
736
      break;
739
            break;
737
  }
740
        }
738
  oq += ss + "\n";
741
        oq += ss + "\n";
739
  if (nlines++ >= maxlines) {
742
        if (nlines++ >= maxlines) {
740
      oq += " ... \n";
743
            oq += " ... \n";
741
      break;
744
            break;
742
  }
745
        }
743
  query= query.substr(ss.length());
746
        query = query.substr(ss.length());
744
    }
747
    }
745
    return oq;
748
    return oq;
746
}
749
}
747
750
748
// Date is Y[-M[-D]]
751
// Date is Y[-M[-D]]
749
static bool parsedate(vector<string>::const_iterator& it, 
752
static bool parsedate(vector<string>::const_iterator& it,
750
              vector<string>::const_iterator end, DateInterval *dip)
753
                      vector<string>::const_iterator end, DateInterval *dip)
751
{
754
{
752
    dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0;
755
    dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0;
753
    if (it->length() > 4 || !it->length() || 
756
    if (it->length() > 4 || !it->length() ||
754
        it->find_first_not_of("0123456789") != string::npos) {
757
            it->find_first_not_of("0123456789") != string::npos) {
755
        return false;
758
        return false;
756
    }
759
    }
757
    if (it == end || sscanf(it++->c_str(), "%d", &dip->y1) != 1) {
760
    if (it == end || sscanf(it++->c_str(), "%d", &dip->y1) != 1) {
758
        return false;
761
        return false;
759
    }
762
    }
760
    if (it == end || *it == "/")
763
    if (it == end || *it == "/") {
761
        return true;
764
        return true;
765
    }
762
    if (*it++ != "-") {
766
    if (*it++ != "-") {
763
        return false;
767
        return false;
764
    }
768
    }
765
769
766
    if (it->length() > 2 || !it->length() || 
770
    if (it->length() > 2 || !it->length() ||
767
        it->find_first_not_of("0123456789") != string::npos) {
771
            it->find_first_not_of("0123456789") != string::npos) {
768
        return false;
772
        return false;
769
    }
773
    }
770
    if (it == end || sscanf(it++->c_str(), "%d", &dip->m1) != 1) {
774
    if (it == end || sscanf(it++->c_str(), "%d", &dip->m1) != 1) {
771
        return false;
775
        return false;
772
    }
776
    }
773
    if (it == end || *it == "/")
777
    if (it == end || *it == "/") {
774
        return true;
778
        return true;
779
    }
775
    if (*it++ != "-") {
780
    if (*it++ != "-") {
776
        return false;
781
        return false;
777
    }
782
    }
778
783
779
    if (it->length() > 2 || !it->length() || 
784
    if (it->length() > 2 || !it->length() ||
780
        it->find_first_not_of("0123456789") != string::npos) {
785
            it->find_first_not_of("0123456789") != string::npos) {
781
        return false;
786
        return false;
782
    }
787
    }
783
    if (it == end || sscanf(it++->c_str(), "%d", &dip->d1) != 1) {
788
    if (it == end || sscanf(it++->c_str(), "%d", &dip->d1) != 1) {
784
        return false;
789
        return false;
785
    }
790
    }
...
...
788
}
793
}
789
794
790
// Called with the 'P' already processed. Period ends at end of string
795
// Called with the 'P' already processed. Period ends at end of string
791
// or at '/'. We dont' do a lot effort at validation and will happily
796
// or at '/'. We dont' do a lot effort at validation and will happily
792
// accept 10Y1Y4Y (the last wins)
797
// accept 10Y1Y4Y (the last wins)
793
static bool parseperiod(vector<string>::const_iterator& it, 
798
static bool parseperiod(vector<string>::const_iterator& it,
794
                        vector<string>::const_iterator end, DateInterval *dip)
799
                        vector<string>::const_iterator end, DateInterval *dip)
795
{
800
{
796
    dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0;
801
    dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0;
797
    while (it != end) {
802
    while (it != end) {
798
        int value;
803
        int value;
...
...
800
            return false;
805
            return false;
801
        }
806
        }
802
        if (sscanf(it++->c_str(), "%d", &value) != 1) {
807
        if (sscanf(it++->c_str(), "%d", &value) != 1) {
803
            return false;
808
            return false;
804
        }
809
        }
805
        if (it == end || it->empty())
810
        if (it == end || it->empty()) {
806
            return false;
811
            return false;
812
        }
807
        switch (it->at(0)) {
813
        switch (it->at(0)) {
808
        case 'Y': case 'y': dip->y1 = value;break;
814
        case 'Y':
809
        case 'M': case 'm': dip->m1 = value;break;
815
        case 'y':
810
        case 'D': case 'd': dip->d1 = value;break;
816
            dip->y1 = value;
817
            break;
818
        case 'M':
819
        case 'm':
820
            dip->m1 = value;
821
            break;
822
        case 'D':
823
        case 'd':
824
            dip->d1 = value;
825
            break;
826
        default:
811
        default: return false;
827
            return false;
812
        }
828
        }
813
        it++;
829
        it++;
814
        if (it == end)
830
        if (it == end) {
815
            return true;
831
            return true;
832
        }
816
        if (*it == "/") {
833
        if (*it == "/") {
817
            return true;
834
            return true;
818
        }
835
        }
819
    }
836
    }
820
    return true;
837
    return true;
821
}
838
}
822
839
823
#ifdef _WIN32
840
#ifdef _WIN32
824
int setenv(const char *name, const char *value, int overwrite)
841
int setenv(const char *name, const char *value, int overwrite)
825
{
842
{
826
    if(!overwrite) {
843
    if (!overwrite) {
827
        const char *cp = getenv(name);
844
        const char *cp = getenv(name);
828
        if (cp)
845
        if (cp) {
829
            return -1;
846
            return -1;
847
        }
830
    }
848
    }
831
    return _putenv_s(name, value);
849
    return _putenv_s(name, value);
832
}
850
}
833
void unsetenv(const char *name)
851
void unsetenv(const char *name)
834
{
852
{
...
...
843
861
844
    tz = getenv("TZ");
862
    tz = getenv("TZ");
845
    setenv("TZ", "", 1);
863
    setenv("TZ", "", 1);
846
    tzset();
864
    tzset();
847
    ret = mktime(tm);
865
    ret = mktime(tm);
848
    if (tz)
866
    if (tz) {
849
        setenv("TZ", tz, 1);
867
        setenv("TZ", tz, 1);
850
    else
868
    } else {
851
        unsetenv("TZ");
869
        unsetenv("TZ");
870
    }
852
    tzset();
871
    tzset();
853
    return ret;
872
    return ret;
854
}
873
}
855
874
856
#if 0
875
#if 0
857
static void cerrdip(const string& s, DateInterval *dip)
876
static void cerrdip(const string& s, DateInterval *dip)
858
{
877
{
859
    cerr << s << dip->y1 << "-" << dip->m1 << "-" << dip->d1 << "/"
878
    cerr << s << dip->y1 << "-" << dip->m1 << "-" << dip->d1 << "/"
860
         << dip->y2 << "-" << dip->m2 << "-" << dip->d2 
879
         << dip->y2 << "-" << dip->m2 << "-" << dip->d2
861
         << endl;
880
         << endl;
862
}
881
}
863
#endif
882
#endif
864
883
865
// Compute date + period. Won't work out of the unix era. 
884
// Compute date + period. Won't work out of the unix era.
866
// or pre-1970 dates. Just convert everything to unixtime and
885
// or pre-1970 dates. Just convert everything to unixtime and
867
// seconds (with average durations for months/years), add and convert
886
// seconds (with average durations for months/years), add and convert
868
// back
887
// back
869
static bool addperiod(DateInterval *dp, DateInterval *pp)
888
static bool addperiod(DateInterval *dp, DateInterval *pp)
870
{
889
{
871
    struct tm tm;
890
    struct tm tm;
872
    // Create a struct tm with possibly non normalized fields and let
891
    // Create a struct tm with possibly non normalized fields and let
873
    // timegm sort it out
892
    // timegm sort it out
874
    memset(&tm, 0, sizeof(tm));
893
    memset(&tm, 0, sizeof(tm));
875
    tm.tm_year = dp->y1 - 1900 + pp->y1;
894
    tm.tm_year = dp->y1 - 1900 + pp->y1;
876
    tm.tm_mon = dp->m1 + pp->m1 -1;
895
    tm.tm_mon = dp->m1 + pp->m1 - 1;
877
    tm.tm_mday = dp->d1 + pp->d1;
896
    tm.tm_mday = dp->d1 + pp->d1;
878
    time_t tres = mktime(&tm);
897
    time_t tres = mktime(&tm);
879
    localtime_r(&tres, &tm);
898
    localtime_r(&tres, &tm);
880
    dp->y1 = tm.tm_year + 1900;
899
    dp->y1 = tm.tm_year + 1900;
881
    dp->m1 = tm.tm_mon + 1;
900
    dp->m1 = tm.tm_mon + 1;
...
...
884
    return true;
903
    return true;
885
}
904
}
886
int monthdays(int mon, int year)
905
int monthdays(int mon, int year)
887
{
906
{
888
    switch (mon) {
907
    switch (mon) {
889
    // We are returning a few two many 29 days februaries, no problem
908
    // We are returning a few too many 29 days februaries, no problem
909
    case 2:
890
    case 2: return (year % 4) == 0 ? 29 : 28;
910
        return (year % 4) == 0 ? 29 : 28;
891
    case 1:case 3:case 5:case 7: case 8:case 10:case 12: return 31;
911
    case 1:
892
    default: return 30;
912
    case 3:
913
    case 5:
914
    case 7:
915
    case 8:
916
    case 10:
917
    case 12:
918
        return 31;
919
    default:
920
        return 30;
893
    }
921
    }
894
}
922
}
895
bool parsedateinterval(const string& s, DateInterval *dip)
923
bool parsedateinterval(const string& s, DateInterval *dip)
896
{
924
{
897
    vector<string> vs;
925
    vector<string> vs;
898
    dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0;
926
    dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0;
899
    DateInterval p1, p2, d1, d2;
927
    DateInterval p1, p2, d1, d2;
900
    p1 = p2 = d1 = d2 = *dip;
928
    p1 = p2 = d1 = d2 = *dip;
901
    bool hasp1 = false, hasp2 = false, hasd1 = false, hasd2 = false, 
929
    bool hasp1 = false, hasp2 = false, hasd1 = false, hasd2 = false,
902
        hasslash = false;
930
         hasslash = false;
903
931
904
    if (!stringToStrings(s, vs, "PYMDpymd-/")) {
932
    if (!stringToStrings(s, vs, "PYMDpymd-/")) {
905
        return false;
933
        return false;
906
    }
934
    }
907
    if (vs.empty())
935
    if (vs.empty()) {
908
        return false;
936
        return false;
937
    }
909
938
910
    vector<string>::const_iterator it = vs.begin();
939
    vector<string>::const_iterator it = vs.begin();
911
    if (*it == "P" || *it == "p") {
940
    if (*it == "P" || *it == "p") {
912
        it++;
941
        it++;
913
        if (!parseperiod(it, vs.end(), &p1)) {
942
        if (!parseperiod(it, vs.end(), &p1)) {
...
...
941
        } else if (*it == "P" || *it == "p") {
970
        } else if (*it == "P" || *it == "p") {
942
            it++;
971
            it++;
943
            if (!parseperiod(it, vs.end(), &p2)) {
972
            if (!parseperiod(it, vs.end(), &p2)) {
944
                return false;
973
                return false;
945
            }
974
            }
946
        hasp2 = true;
975
            hasp2 = true;
947
        } else {
976
        } else {
948
            if (!parsedate(it, vs.end(), &d2)) {
977
            if (!parsedate(it, vs.end(), &d2)) {
949
                return false;
978
                return false;
950
            }
979
            }
951
            hasd2 = true;
980
            hasd2 = true;
...
...
981
    // a period or not (actual or infinite indicated by a / + empty)
1010
    // a period or not (actual or infinite indicated by a / + empty)
982
    //
1011
    //
983
    // If there is no explicit period, an incomplete date indicates a
1012
    // If there is no explicit period, an incomplete date indicates a
984
    // period of the size of the uncompleted elements. Ex: 1999
1013
    // period of the size of the uncompleted elements. Ex: 1999
985
    // actually means 1999/P12M
1014
    // actually means 1999/P12M
986
    // 
1015
    //
987
    // If there is a period, the incomplete date should be extended
1016
    // If there is a period, the incomplete date should be extended
988
    // to the beginning or end of the unspecified portion. Ex: 1999/
1017
    // to the beginning or end of the unspecified portion. Ex: 1999/
989
    // means 1999-01-01/ and /1999 means /1999-12-31
1018
    // means 1999-01-01/ and /1999 means /1999-12-31
990
    if (hasd1) {
1019
    if (hasd1) {
991
        if (!(hasslash || hasp2)) {
1020
        if (!(hasslash || hasp2)) {
...
...
1040
}
1069
}
1041
1070
1042
1071
1043
void catstrerror(string *reason, const char *what, int _errno)
1072
void catstrerror(string *reason, const char *what, int _errno)
1044
{
1073
{
1045
    if (!reason)
1074
    if (!reason) {
1046
  return;
1075
        return;
1076
    }
1047
    if (what)
1077
    if (what) {
1048
  reason->append(what);
1078
        reason->append(what);
1079
    }
1049
1080
1050
    reason->append(": errno: ");
1081
    reason->append(": errno: ");
1051
1082
1052
    char nbuf[20];
1083
    char nbuf[20];
1053
    sprintf(nbuf, "%d", _errno);
1084
    sprintf(nbuf, "%d", _errno);
...
...
1057
1088
1058
#if defined(sun) || defined(_WIN32)
1089
#if defined(sun) || defined(_WIN32)
1059
    // Note: sun strerror is noted mt-safe ??
1090
    // Note: sun strerror is noted mt-safe ??
1060
    reason->append(strerror(_errno));
1091
    reason->append(strerror(_errno));
1061
#else
1092
#else
1062
#define ERRBUFSZ 200    
1093
#define ERRBUFSZ 200
1063
    char errbuf[ERRBUFSZ];
1094
    char errbuf[ERRBUFSZ];
1064
    // There are 2 versions of strerror_r. 
1095
    // There are 2 versions of strerror_r.
1065
    // - The GNU one returns a pointer to the message (maybe
1096
    // - The GNU one returns a pointer to the message (maybe
1066
    //   static storage or supplied buffer).
1097
    //   static storage or supplied buffer).
1067
    // - The POSIX one always stores in supplied buffer and
1098
    // - The POSIX one always stores in supplied buffer and
1068
    //   returns 0 on success. As the possibility of error and
1099
    //   returns 0 on success. As the possibility of error and
1069
    //   error code are not specified, we're basically doomed
1100
    //   error code are not specified, we're basically doomed
1070
    //   cause we can't use a test on the 0 value to know if we
1101
    //   cause we can't use a test on the 0 value to know if we
1071
    //   were returned a pointer... 
1102
    //   were returned a pointer...
1072
    // Also couldn't find an easy way to disable the gnu version without
1103
    // Also couldn't find an easy way to disable the gnu version without
1073
    // changing the cxxflags globally, so forget it. Recent gnu lib versions
1104
    // changing the cxxflags globally, so forget it. Recent gnu lib versions
1074
    // normally default to the posix version.
1105
    // normally default to the posix version.
1075
    // At worse we get no message at all here.
1106
    // At worse we get no message at all here.
1076
    errbuf[0] = 0;
1107
    errbuf[0] = 0;
...
...
1078
    char *ret = (char *)strerror_r(_errno, errbuf, ERRBUFSZ);
1109
    char *ret = (char *)strerror_r(_errno, errbuf, ERRBUFSZ);
1079
    reason->append(errbuf);
1110
    reason->append(errbuf);
1080
#endif
1111
#endif
1081
}
1112
}
1082
1113
1083
void HighlightData::toString(std::string& out)
1084
{
1085
    out.append("\nUser terms (orthograph): ");
1086
    for (std::set<std::string>::const_iterator it = uterms.begin();
1087
   it != uterms.end(); it++) {
1088
  out.append(" [").append(*it).append("]");
1089
    }
1090
    out.append("\nUser terms to Query terms:");
1091
    for (map<string, string>::const_iterator it = terms.begin();
1092
   it != terms.end(); it++) {
1093
  out.append("[").append(it->first).append("]->[");
1094
  out.append(it->second).append("] ");
1095
    }
1096
    out.append("\nGroups: ");
1097
    char cbuf[200];
1098
    sprintf(cbuf, "Groups size %d grpsugidx size %d ugroups size %d",
1099
      int(groups.size()), int(grpsugidx.size()), int(ugroups.size()));
1100
    out.append(cbuf);
1101
1102
    size_t ugidx = (size_t)-1;
1103
    for (unsigned int i = 0; i < groups.size(); i++) {
1104
  if (ugidx != grpsugidx[i]) {
1105
      ugidx = grpsugidx[i];
1106
      out.append("\n(");
1107
      for (unsigned int j = 0; j < ugroups[ugidx].size(); j++) {
1108
      out.append("[").append(ugroups[ugidx][j]).append("] ");
1109
      }
1110
      out.append(") ->");
1111
  }
1112
  out.append(" {");
1113
  for (unsigned int j = 0; j < groups[i].size(); j++) {
1114
      out.append("[").append(groups[i][j]).append("]");
1115
  }
1116
  sprintf(cbuf, "%d", slacks[i]);
1117
  out.append("}").append(cbuf);
1118
    }
1119
    out.append("\n");
1120
}
1121
1122
void HighlightData::append(const HighlightData& hl)
1123
{
1124
    uterms.insert(hl.uterms.begin(), hl.uterms.end());
1125
    terms.insert(hl.terms.begin(), hl.terms.end());
1126
    size_t ugsz0 = ugroups.size();
1127
    ugroups.insert(ugroups.end(), hl.ugroups.begin(), hl.ugroups.end());
1128
1129
    groups.insert(groups.end(), hl.groups.begin(), hl.groups.end());
1130
    slacks.insert(slacks.end(), hl.slacks.begin(), hl.slacks.end());
1131
    for (std::vector<size_t>::const_iterator it = hl.grpsugidx.begin(); 
1132
   it != hl.grpsugidx.end(); it++) {
1133
  grpsugidx.push_back(*it + ugsz0);
1134
    }
1135
}
1136
1114
1137
static const char *vlang_to_code[] = {
1115
static const char *vlang_to_code[] = {
1138
    "be", "cp1251",
1116
    "be", "cp1251",
1139
    "bg", "cp1251",
1117
    "bg", "cp1251",
1140
    "cs", "iso-8859-2",
1118
    "cs", "iso-8859-2",
...
...
1157
    "th", "iso-8859-11",
1135
    "th", "iso-8859-11",
1158
    "tr", "iso-8859-9",
1136
    "tr", "iso-8859-9",
1159
    "uk", "koi8-u",
1137
    "uk", "koi8-u",
1160
};
1138
};
1161
1139
1140
static const string cstr_cp1252("CP1252");
1141
1162
string langtocode(const string& lang)
1142
string langtocode(const string& lang)
1163
{
1143
{
1164
    static STD_UNORDERED_MAP<string, string> lang_to_code;
1144
    static STD_UNORDERED_MAP<string, string> lang_to_code;
1165
    if (lang_to_code.empty()) {
1145
    if (lang_to_code.empty()) {
1166
  for (unsigned int i = 0; 
1146
        for (unsigned int i = 0;
1167
       i < sizeof(vlang_to_code) / sizeof(char *); i += 2) {
1147
                i < sizeof(vlang_to_code) / sizeof(char *); i += 2) {
1168
      lang_to_code[vlang_to_code[i]] = vlang_to_code[i+1];
1148
            lang_to_code[vlang_to_code[i]] = vlang_to_code[i + 1];
1169
  }
1170
    }
1149
        }
1150
    }
1171
    STD_UNORDERED_MAP<string,string>::const_iterator it = 
1151
    STD_UNORDERED_MAP<string, string>::const_iterator it =
1172
  lang_to_code.find(lang);
1152
        lang_to_code.find(lang);
1173
1153
1174
    // Use cp1252 by default...
1154
    // Use cp1252 by default...
1175
    if (it == lang_to_code.end())
1155
    if (it == lang_to_code.end()) {
1176
  return cstr_cp1252;
1156
        return cstr_cp1252;
1157
    }
1177
1158
1178
    return it->second;
1159
    return it->second;
1179
}
1160
}
1180
1161
1181
string localelang()
1162
string localelang()
1182
{
1163
{
1183
    const char *lang = getenv("LANG");
1164
    const char *lang = getenv("LANG");
1184
1165
1185
    if (lang == 0 || *lang == 0 || !strcmp(lang, "C") || !strcmp(lang, "POSIX"))
1166
    if (lang == 0 || *lang == 0 || !strcmp(lang, "C") ||
1186
  return "en";
1167
            !strcmp(lang, "POSIX")) {
1168
        return "en";
1169
    }
1187
    string locale(lang);
1170
    string locale(lang);
1188
    string::size_type under = locale.find_first_of("_");
1171
    string::size_type under = locale.find_first_of("_");
1189
    if (under == string::npos)
1172
    if (under == string::npos) {
1190
  return locale;
1173
        return locale;
1174
    }
1191
    return locale.substr(0, under);
1175
    return locale.substr(0, under);
1192
}
1176
}
1193
1177
1194
// Initialization for static stuff to be called from main thread before going 
1178
// Initialization for static stuff to be called from main thread before going
1195
// multiple
1179
// multiple
1196
void smallut_init_mt()
1180
void smallut_init_mt()
1197
{
1181
{
1198
    // Init langtocode() static table
1182
    // Init langtocode() static table
1199
    langtocode("");
1183
    langtocode("");
...
...
1240
1224
1241
1225
1242
// Periods test strings
1226
// Periods test strings
1243
const char* periods[] = {
1227
const char* periods[] = {
1244
    "2001",    // Year 2001
1228
    "2001",    // Year 2001
1245
    "2001/",  // 2001 or later 
1229
    "2001/",  // 2001 or later
1246
    "2001/P3Y", // 2001 -> 2004 or 2005, ambiguous
1230
    "2001/P3Y", // 2001 -> 2004 or 2005, ambiguous
1247
    "2001-01-01/P3Y", // 01-2001 -> 01 2004
1231
    "2001-01-01/P3Y", // 01-2001 -> 01 2004
1248
    "2001-03-03/2001-05-01", // Explicit one
1232
    "2001-03-03/2001-05-01", // Explicit one
1249
    "P3M/", // 3 months ago to now
1233
    "P3M/", // 3 months ago to now
1250
    "P1Y1M/2001-03-01", // 2000-02-01/2001-03-01
1234
    "P1Y1M/2001-03-01", // 2000-02-01/2001-03-01
...
...
1254
1238
1255
const char *thisprog;
1239
const char *thisprog;
1256
static void cerrdip(const string& s, DateInterval *dip)
1240
static void cerrdip(const string& s, DateInterval *dip)
1257
{
1241
{
1258
    cerr << s << dip->y1 << "-" << dip->m1 << "-" << dip->d1 << "/"
1242
    cerr << s << dip->y1 << "-" << dip->m1 << "-" << dip->d1 << "/"
1259
         << dip->y2 << "-" << dip->m2 << "-" << dip->d2 
1243
         << dip->y2 << "-" << dip->m2 << "-" << dip->d2
1260
         << endl;
1244
         << endl;
1261
}
1245
}
1262
1246
1263
int main(int argc, char **argv)
1247
int main(int argc, char **argv)
1264
{
1248
{
1265
    thisprog = *argv++;argc--;
1249
    thisprog = *argv++;
1250
    argc--;
1266
1251
1267
#if 1
1252
#if 1
1268
    if (argc <=0 ) {
1253
    if (argc <= 0) {
1269
        cerr << "Usage: smallut <stringtosplit>" << endl;
1254
        cerr << "Usage: smallut <stringtosplit>" << endl;
1270
        exit(1);
1255
        exit(1);
1271
    }
1256
    }
1272
    string s = *argv++;argc--;
1257
    string s = *argv++;
1258
    argc--;
1273
    vector<string> vs;
1259
    vector<string> vs;
1274
    stringToTokens(s, vs, "/");
1260
    stringToTokens(s, vs, "/");
1275
    for (vector<string>::const_iterator it = vs.begin(); it != vs.end(); it++)
1261
    for (vector<string>::const_iterator it = vs.begin(); it != vs.end(); it++) {
1276
        cerr << "[" << *it << "] ";
1262
        cerr << "[" << *it << "] ";
1263
    }
1277
    cerr << endl;
1264
    cerr << endl;
1278
    exit(0);
1265
    exit(0);
1279
#elif 0
1266
#elif 0
1280
    if (argc <=0 ) {
1267
    if (argc <= 0) {
1281
        cerr << "Usage: smallut <stringtosplit>" << endl;
1268
        cerr << "Usage: smallut <stringtosplit>" << endl;
1282
        exit(1);
1269
        exit(1);
1283
    }
1270
    }
1284
    string s = *argv++;argc--;
1271
    string s = *argv++;
1272
    argc--;
1285
    vector<string> vs;
1273
    vector<string> vs;
1286
    if (!stringToStrings(s, vs, ":-()")) {
1274
    if (!stringToStrings(s, vs, ":-()")) {
1287
        cerr << "Bad entry" << endl;
1275
        cerr << "Bad entry" << endl;
1288
        exit(1);
1276
        exit(1);
1289
    }
1277
    }
1290
    for (vector<string>::const_iterator it = vs.begin(); it != vs.end(); it++)
1278
    for (vector<string>::const_iterator it = vs.begin(); it != vs.end(); it++) {
1291
        cerr << "[" << *it << "] ";
1279
        cerr << "[" << *it << "] ";
1280
    }
1292
    cerr << endl;
1281
    cerr << endl;
1293
    exit(0);
1282
    exit(0);
1294
#elif 0
1283
#elif 0
1295
    if (argc <=0 ) {
1284
    if (argc <= 0) {
1296
        cerr << "Usage: smallut <dateinterval>" << endl;
1285
        cerr << "Usage: smallut <dateinterval>" << endl;
1297
        exit(1);
1286
        exit(1);
1298
    }
1287
    }
1299
    string s = *argv++;argc--;
1288
    string s = *argv++;
1289
    argc--;
1300
    DateInterval di;
1290
    DateInterval di;
1301
    if (!parsedateinterval(s, &di)) {
1291
    if (!parsedateinterval(s, &di)) {
1302
        cerr << "Parse failed" << endl;
1292
        cerr << "Parse failed" << endl;
1303
        exit(1);
1293
        exit(1);
1304
    }
1294
    }
...
...
1314
        }
1304
        }
1315
    }
1305
    }
1316
    exit(0);
1306
    exit(0);
1317
#elif 0
1307
#elif 0
1318
    for (int i = 0; i < npairs; i++) {
1308
    for (int i = 0; i < npairs; i++) {
1319
  {
1309
        {
1320
      int c = stringicmp(pairs[i].s1, pairs[i].s2);
1310
            int c = stringicmp(pairs[i].s1, pairs[i].s2);
1321
      printf("'%s' %s '%s' ", pairs[i].s1, 
1311
            printf("'%s' %s '%s' ", pairs[i].s1,
1322
         c == 0 ? "==" : c < 0 ? "<" : ">", pairs[i].s2);
1312
                   c == 0 ? "==" : c < 0 ? "<" : ">", pairs[i].s2);
1323
  }
1313
        }
1324
  {
1314
        {
1325
      int cl = stringlowercmp(pairs[i].s1, pairs[i].s2);
1315
            int cl = stringlowercmp(pairs[i].s1, pairs[i].s2);
1326
      printf("L '%s' %s '%s' ", pairs[i].s1, 
1316
            printf("L '%s' %s '%s' ", pairs[i].s1,
1327
         cl == 0 ? "==" : cl < 0 ? "<" : ">", pairs[i].s2);
1317
                   cl == 0 ? "==" : cl < 0 ? "<" : ">", pairs[i].s2);
1328
  }
1318
        }
1329
  {
1319
        {
1330
      int cu = stringuppercmp(pairs[i].s1, pairs[i].s2);
1320
            int cu = stringuppercmp(pairs[i].s1, pairs[i].s2);
1331
      printf("U '%s' %s '%s' ", pairs[i].s1, 
1321
            printf("U '%s' %s '%s' ", pairs[i].s1,
1332
         cu == 0 ? "==" : cu < 0 ? "<" : ">", pairs[i].s2);
1322
                   cu == 0 ? "==" : cu < 0 ? "<" : ">", pairs[i].s2);
1333
  }
1323
        }
1334
  printf("\n");
1324
        printf("\n");
1335
    }
1325
    }
1336
#elif 0
1326
#elif 0
1337
    for (int i = 0; i < nsuffpairs; i++) {
1327
    for (int i = 0; i < nsuffpairs; i++) {
1338
  int c = stringisuffcmp(suffpairs[i].s1, suffpairs[i].s2);
1328
        int c = stringisuffcmp(suffpairs[i].s1, suffpairs[i].s2);
1339
  printf("[%s] %s [%s] \n", suffpairs[i].s1, 
1329
        printf("[%s] %s [%s] \n", suffpairs[i].s1,
1340
         c == 0 ? "matches" : c < 0 ? "<" : ">", suffpairs[i].s2);
1330
               c == 0 ? "matches" : c < 0 ? "<" : ">", suffpairs[i].s2);
1341
    }
1331
    }
1342
#elif 0
1332
#elif 0
1343
    std::string testit("\303\251l\303\251gant");
1333
    std::string testit("\303\251l\303\251gant");
1344
    for (int sz = 10; sz >= 0; sz--) {
1334
    for (int sz = 10; sz >= 0; sz--) {
1345
  utf8truncate(testit, sz);
1335
        utf8truncate(testit, sz);
1346
  cout << testit << endl;
1336
        cout << testit << endl;
1347
    }
1337
    }
1348
#elif 0
1338
#elif 0
1349
    std::string testit("ligne\ndeuxieme ligne\r3eme ligne\r\n");
1339
    std::string testit("ligne\ndeuxieme ligne\r3eme ligne\r\n");
1350
    cout << "[" << neutchars(testit, "\r\n") << "]" << endl;
1340
    cout << "[" << neutchars(testit, "\r\n") << "]" << endl;
1351
    string i, o;
1341
    string i, o;
...
...
1383
    cout << "CSV line: [" << out << "]" << endl;
1373
    cout << "CSV line: [" << out << "]" << endl;
1384
#elif 0
1374
#elif 0
1385
    string sshort("ABC");
1375
    string sshort("ABC");
1386
    string slong("ABCD");
1376
    string slong("ABCD");
1387
    string sshortsmaller("ABB");
1377
    string sshortsmaller("ABB");
1388
    
1378
1389
    vector<pair<string,string> > cmps;
1379
    vector<pair<string, string> > cmps;
1390
    cmps.push_back(pair<string,string>(sshort,sshort));
1380
    cmps.push_back(pair<string, string>(sshort, sshort));
1391
    cmps.push_back(pair<string,string>(sshort,slong));
1381
    cmps.push_back(pair<string, string>(sshort, slong));
1392
    cmps.push_back(pair<string,string>(slong,sshort));
1382
    cmps.push_back(pair<string, string>(slong, sshort));
1393
    cmps.push_back(pair<string,string>(sshortsmaller,sshort));
1383
    cmps.push_back(pair<string, string>(sshortsmaller, sshort));
1394
    cmps.push_back(pair<string,string>(sshort, sshortsmaller));
1384
    cmps.push_back(pair<string, string>(sshort, sshortsmaller));
1395
1385
1396
    for (vector<pair<string,string> >::const_iterator it = cmps.begin();
1386
    for (vector<pair<string, string> >::const_iterator it = cmps.begin();
1397
         it != cmps.end(); it++) {
1387
            it != cmps.end(); it++) {
1398
        cout << it->first << " " << it->second << " " << 
1388
        cout << it->first << " " << it->second << " " <<
1399
            stringicmp(it->first, it->second) << endl;
1389
             stringicmp(it->first, it->second) << endl;
1400
    }
1390
    }
1401
    cout << endl;
1391
    cout << endl;
1402
    for (vector<pair<string,string> >::const_iterator it = cmps.begin();
1392
    for (vector<pair<string, string> >::const_iterator it = cmps.begin();
1403
         it != cmps.end(); it++) {
1393
            it != cmps.end(); it++) {
1404
        cout << it->first << " " << it->second << " " << 
1394
        cout << it->first << " " << it->second << " " <<
1405
            stringlowercmp(stringtolower(it->first), it->second) << endl;
1395
             stringlowercmp(stringtolower(it->first), it->second) << endl;
1406
    }
1396
    }
1407
    cout << endl;
1397
    cout << endl;
1408
    for (vector<pair<string,string> >::const_iterator it = cmps.begin();
1398
    for (vector<pair<string, string> >::const_iterator it = cmps.begin();
1409
         it != cmps.end(); it++) {
1399
            it != cmps.end(); it++) {
1410
        cout << it->first << " " << it->second << " " << 
1400
        cout << it->first << " " << it->second << " " <<
1411
            stringuppercmp(it->first, it->second) << endl;
1401
             stringuppercmp(it->first, it->second) << endl;
1412
    }
1402
    }
1413
1403
1414
#endif
1404
#endif
1415
}
1405
}
1416
1406