pmlist.cc 9.81 KB
Newer Older
1
2
3
4
/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

Uwe Schulzweida's avatar
Uwe Schulzweida committed
5
  Copyright (C) 2003-2020 Uwe Schulzweida, <uwe.schulzweida AT mpimet.mpg.de>
6
7
8
9
10
11
12
13
14
15
16
  See COPYING file for copying and redistribution conditions.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
17

Uwe Schulzweida's avatar
Uwe Schulzweida committed
18
19
#include <string.h>
#include <stdlib.h>
20
#include <vector>
Uwe Schulzweida's avatar
Uwe Schulzweida committed
21
22

#include "pmlist.h"
23
#include "namelist.h"
24
#include "cdo_output.h"
25
26
27
28
29
30
31
32
33
34

static void
keyValuesPrint(const KeyValues &keyValues)
{
  printf("  %s =", keyValues.key.c_str());
  for (int i = 0; i < keyValues.nvalues; ++i) printf(" '%s'", keyValues.values[i].c_str());
  printf("\n");
}

void
35
KVList::print() const
36
37
38
39
40
{
  for (const auto &keyValues : *this) keyValuesPrint(keyValues);
}

int
41
KVList::parseArguments(const int argc, const std::vector<std::string> &argv)
42
43
44
45
46
47
{
  // Assume key = value pairs. That is, if argv[i] contains no '=' then treat it as if it belongs to the values of argv[i-1].
  char key[256];
  int i = 0;
  while (i < argc)
    {
48
49
      const char *currentArgv = argv[i].c_str();
      const char *end = strchr(currentArgv, '=');
Uwe Schulzweida's avatar
Uwe Schulzweida committed
50
      if (end == nullptr)
51
        {
52
          fprintf(stderr, "Missing '=' in key/value string: >%s<\n", currentArgv);
53
54
55
          return -1;
        }

56
      snprintf(key, sizeof(key), "%.*s", (int) (end - currentArgv), currentArgv);
57
58
59
      key[sizeof(key) - 1] = 0;

      int j = 1;
60
      while (i + j < argc && strchr(argv[i + j].c_str(), '=') == nullptr) j++;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

      int nvalues = j;

      KeyValues kv;
      kv.values.resize(1);
      kv.values[0] = end + 1;
      if (kv.values[0][0] == 0) nvalues = 0;

      kv.key = key;
      kv.nvalues = nvalues;
      kv.values.resize(nvalues);

      for (j = 1; j < nvalues; ++j) kv.values[j] = argv[i + j];
      this->push_back(kv);

      i += j;
    }

  return 0;
}

82
const KeyValues *
83
KVList::search(const std::string &key) const
Uwe Schulzweida's avatar
Uwe Schulzweida committed
84
{
85
  for (const auto &kv : *this)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
86
    {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
87
      if (kv.key == key) return &kv;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
88
89
    }

Uwe Schulzweida's avatar
Uwe Schulzweida committed
90
  return nullptr;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
91
}
92

93
94
95
96
char *
KVList::get_first_value(const char *key, const char *replacer)
{
  const KeyValues *kv = this->search(key);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
97
  if (kv && kv->nvalues > 0) return (char *) kv->values[0].c_str();
98
99
100
101
102
103
  if (replacer)
    return (char *) replacer;
  else
    return nullptr;
}

104
void
105
KVList::append(const char *key, const char *const *values, int nvalues)
106
107
108
109
110
111
112
113
114
{
  KeyValues kv;
  kv.key = strdup(key);
  kv.nvalues = nvalues;
  kv.values.resize(nvalues);
  for (int i = 0; i < nvalues; ++i) kv.values[i] = values[i];
  this->push_back(kv);
}

115
/* Remove only one list item */
116
void
117
KVList::remove(const std::string &inkey)
118
{
119
  std::list<KeyValues>::iterator i;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
120
121
122
  for (i = this->begin(); i != this->end(); i++)
    if (i->key == inkey) break;
  if (i->key == inkey) this->erase(i);
123
124
}

125
const KVList *
126
PMList::searchKVListVentry(const std::string &key, const std::string &value, const std::vector<std::string> &entry)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
127
{
128
  for (const auto &kvlist : *this)
129
    {
130
      for (const auto &s : entry)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
131
        if (kvlist.name == s)
132
          {
133
            const KeyValues *kv = kvlist.search(key);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
134
            if (kv && kv->nvalues > 0 && kv->values[0] == value) return &kvlist;
135
          }
136
    }
137

Uwe Schulzweida's avatar
Uwe Schulzweida committed
138
  return nullptr;
139
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
140

141
const KVList *
142
PMList::getKVListVentry(const std::vector<std::string> &entry)
143
144
145
{
  for (const auto &kvlist : *this)
    {
146
      for (const auto &s : entry)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
147
        if (kvlist.name == s) return &kvlist;
148
    }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
149

Uwe Schulzweida's avatar
Uwe Schulzweida committed
150
  return nullptr;
151
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
152

153
static void
154
KVListAppendNamelist(KVList &kvlist, const char *key, const char *buffer, NamelistToken *t, int nvalues, bool cdocmor)
155
156
157
158
159
160
161
{
  char vbuf[4096];
  std::vector<char> value;
  KeyValues kv;
  kv.key = key;
  kv.nvalues = nvalues;
  if (nvalues > 0) kv.values.resize(nvalues);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
162

163
164
  for (int i = 0; i < nvalues; ++i)
    {
165
      const size_t len = t[i].end - t[i].start;
166
      /**CMOR_MAX_STRING cannot be used **/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
167
      if (cdocmor && len > 1024) cdoAbort("A string value is larger than the maximum size allowed by CMOR (1024 signs).");
168

169
170
171
      value.resize(len + 1);
      // printf(" value[%d] >%.*s<\n", i, (int)len, buffer+t[i].start);
      const char *pval = buffer + t[i].start;
172

173
174
175
176
177
178
      if (len < sizeof(vbuf))  // snprintf seems to call strlen(pval)
        {
          memcpy(vbuf, buffer + t[i].start, len);
          vbuf[len] = 0;
          pval = vbuf;
        }
179

Uwe Schulzweida's avatar
Uwe Schulzweida committed
180
      if (cdocmor && strcmp(key, "code") == 0)
181
        {
182
          const int code = atoi(pval);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
183
          if (code > 0 && code < 1000)
184
            snprintf(value.data(), 4, "%03d", code);
185
186
187
188
189
          else
            {
              cdoWarning("In parsing a line of a file:\n          "
                         "Codes could not be transformed into the code format (three digit integer). Codes wont be used.");
            }
190
191
192
193
194
195
        }
      else
        {
          snprintf(value.data(), len + 1, "%.*s", (int) len, pval);
          value[len] = 0;
        }
196
197
      kv.values[i] = value.data();
    }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
198

199
200
  kvlist.push_back(kv);
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
201

202
203
204
205
static int
getNumberOfValues(const int ntok, const NamelistToken *tokens)
{
  int it;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
206

207
208
  for (it = 0; it < ntok; ++it)
    {
209
      auto type = tokens[it].type;
210
211
      if (type != NamelistType::WORD && type != NamelistType::STRING) break;
    }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
212

213
214
  return it;
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
215

216
217
218
static void
replaceName(char *name)
{
219
220
221
222
223
  for ( size_t pos = 0; pos < strlen(name); pos++ )
    {
      name[pos] = tolower(name[pos]);
    }

224
225
226
227
228
229
230
231
232
233
234
  if (strcmp(name, "conventions") == 0) strcpy(name, "Conventions");
  if (strcmp(name, "cn") == 0) strcpy(name, "cmor_name");
  if (strcmp(name, "c") == 0) strcpy(name, "code");
  if (strcmp(name, "n") == 0) strcpy(name, "name");
  if (strcmp(name, "pmt") == 0) strcpy(name, "project_mip_table");
  if (strcmp(name, "cordex_domain") == 0) strcpy(name, "CORDEX_domain");
  if (strcmp(name, "char_axis_landuse") == 0) strcpy(name, "char_axis_landUse");
}

int
parseNamelist(PMList &pmlist, NamelistParser &parser, char *buf, bool cdocmor)
235
236
237
{
  char name[4096];
  KVList kvlist;
238
  auto &tokens = parser.tokens;
239
240
  const int ntok = parser.toknext;
  // printf("Number of tokens %d\n", ntok);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
241

242
243
  for (int it = 0; it < ntok; ++it)
    {
244
      const auto &t = tokens[it];
245
246
247
248
249
250
251
      // printf("Token %u", it+1);
      if (t.type == NamelistType::OBJECT)
        {
          name[0] = 0;
          if (it + 1 < ntok && tokens[it + 1].type == NamelistType::WORD)
            {
              it++;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
252
253
              const auto &t2 = tokens[it];
              snprintf(name, sizeof(name), "%.*s", t2.end - t2.start, buf + t2.start);
254
255
              name[sizeof(name) - 1] = 0;
            }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
256

257
258
259
260
261
          if (kvlist.size())
            {
              pmlist.push_back(kvlist);
              kvlist.clear();
            }
262

263
264
265
266
267
268
269
          kvlist.name = name;
        }
      else if (t.type == NamelistType::KEY)
        {
          // printf(" key >%.*s<\n", t.end - t.start, buf+t.start);
          snprintf(name, sizeof(name), "%.*s", t.end - t.start, buf + t.start);
          name[sizeof(name) - 1] = 0;
270
          const auto nvalues = getNumberOfValues(ntok - it - 1, &tokens[it + 1]);
271

Uwe Schulzweida's avatar
Uwe Schulzweida committed
272
          if (cdocmor)
273
            {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
274
              if (nvalues == 0)
275
276
277
278
279
280
281
282
                {
                  cdoWarning("Could not find values for key '%s'.", name);
                  continue;
                }
              replaceName(name);
            }

          KVListAppendNamelist(kvlist, name, buf, &tokens[it + 1], nvalues, cdocmor);
283
284
285
286
287
288
289
290
          it += nvalues;
        }
      else
        {
          // printf(" token >%.*s<\n", t.end - t.start, buf+t.start);
          break;
        }
    }
291

292
  if (kvlist.size()) pmlist.push_back(kvlist);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
293

294
295
  return 0;
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
296

297
int
298
parseListBuffer(NamelistParser &p, ListBuffer &listBuffer)
299
300
{
  const char *errMsg = "Namelist error";
301
  const auto name = listBuffer.name.c_str();
302

303
  const auto status = p.parse(listBuffer.buffer.data(), listBuffer.buffer.size());
304
  if (status != NamelistError::UNDEFINED)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
305
    {
306
307
308
      switch (status)
        {
        case NamelistError::INVAL:
309
310
          fprintf(stderr, "%s: Invalid character in %s (line=%d character='%c' dec=%u)!\n", errMsg, name, p.lineno,
                  listBuffer.buffer[p.pos], (unsigned char)listBuffer.buffer[p.pos]);
311
          break;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
312
        case NamelistError::PART: fprintf(stderr, "%s: End of string not found in %s (line=%d)!\n", errMsg, name, p.lineno); break;
313
        case NamelistError::INKEY: fprintf(stderr, "%s: Invalid key word in %s (line=%d)!\n", errMsg, name, p.lineno); break;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
314
        case NamelistError::INTYP: fprintf(stderr, "%s: Invalid key word type in %s (line=%d)!\n", errMsg, name, p.lineno); break;
315
        case NamelistError::INOBJ: fprintf(stderr, "%s: Invalid object in %s (line=%d)!\n", errMsg, name, p.lineno); break;
Fabian Wachsmann's avatar
Fabian Wachsmann committed
316
        case NamelistError::EMKEY: fprintf(stderr, "%s: Empty key name in %s (line=%d)!\n", errMsg, name, p.lineno); break;
317
318
319
        default: fprintf(stderr, "%s in %s (line=%d)!\n", errMsg, name, p.lineno); break;
        }
      cdoAbort("%s!", errMsg);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
320
321
    }

322
323
  // p.dump(listBuffer.buffer.data());
  if (p.verify())
Uwe Schulzweida's avatar
Uwe Schulzweida committed
324
    {
325
326
      fprintf(stderr, "%s: Invalid contents in %s!\n", errMsg, name);
      cdoAbort("Namelist error!");
Uwe Schulzweida's avatar
Uwe Schulzweida committed
327
328
329
330
    }

  return 0;
}
331
332
333
334
335
336
337

void
PMList::readNamelist(FILE *fp, const char *name)
{
  ListBuffer listBuffer;
  if (listBuffer.read(fp, name)) cdoAbort("Read error on namelist %s!", name);

Fabian Wachsmann's avatar
Fabian Wachsmann committed
338
339
  NamelistParser p;
  const auto status = parseListBuffer(p, listBuffer);
340
  if (status) cdoAbort("Namelist not found!");
Fabian Wachsmann's avatar
Fabian Wachsmann committed
341
342

  parseNamelist(*this, p, listBuffer.buffer.data(), false);
343
344
345
346
347
}

void
PMList::print()
{
348
  for (const auto &kvlist : *this)
349
    {
350
      const auto listname = kvlist.name.c_str();
351
352
353
354
      printf("\nFound %s list with %zu key/values: \n", listname ? listname : "", kvlist.size());
      kvlist.print();
    }
}