Statistics
| Branch: | Revision:

root / src / common / displaystring.cc @ e1750c09

History | View | Annotate | Download (10 KB)

1
//==========================================================================
2
//   DISPLAYSTRING.CC  - part of
3
//                     OMNeT++/OMNEST
4
//            Discrete System Simulation in C++
5
//
6
//  Author: Andras Varga
7
//
8
//==========================================================================
9

    
10
/*--------------------------------------------------------------*
11
  Copyright (C) 2006-2008 OpenSim Ltd.
12

13
  This file is distributed WITHOUT ANY WARRANTY. See the file
14
  `license' for details on this and other legal matters.
15
*--------------------------------------------------------------*/
16

    
17
#include <string.h>
18
#include <stdio.h>
19
#include "opp_ctype.h"
20
#include "displaystring.h"
21
#include "stringutil.h"
22

    
23
USING_NAMESPACE
24

    
25

    
26
DisplayString::DisplayString()
27
{
28
    dispstr = NULL;
29
    buffer = NULL;
30
    bufferend = NULL;
31
    tags = NULL;
32
    numtags = 0;
33
    needsassemble = false;
34
}
35

    
36

    
37
DisplayString::DisplayString(const char *displaystr)
38
{
39
    dispstr = opp_strdup(displaystr);
40
    buffer = NULL;
41
    bufferend = NULL;
42
    tags = NULL;
43
    numtags = 0;
44
    needsassemble = false;
45

    
46
    parse();
47
}
48

    
49
DisplayString::DisplayString(const DisplayString& ds)
50
{
51
    dispstr = NULL;
52
    buffer = NULL;
53
    bufferend = NULL;
54
    tags = NULL;
55
    numtags = 0;
56
    needsassemble = false;
57

    
58
    operator=(ds);
59
}
60

    
61
DisplayString::~DisplayString()
62
{
63
    delete [] dispstr;
64
    cleartags();
65
}
66

    
67
const char *DisplayString::str() const
68
{
69
    if (needsassemble)
70
        assemble();
71
    return dispstr ? dispstr : "";
72
}
73

    
74

    
75
bool DisplayString::parse(const char *displaystr)
76
{
77
    // if it's the same, nothing to do
78
    if (needsassemble)
79
        assemble();
80
    if (!opp_strcmp(dispstr,displaystr))
81
        return true;
82

    
83
    // parse and store new string
84
    delete [] dispstr;
85
    cleartags();
86
    dispstr = opp_strdup(displaystr);
87
    bool fullyOK = parse();
88

    
89
    return fullyOK;
90
}
91

    
92

    
93
void DisplayString::updateWith(const DisplayString& ds)
94
{
95
    // elements in "ds" take precedence
96
    int n = ds.getNumTags();
97
    for (int i=0; i<n; i++)
98
    {
99
        int m = ds.getNumArgs(i);
100
        for (int j=0; j<m; j++)
101
        {
102
            const char *arg = ds.getTagArg(i,j);
103
            if (arg[0])
104
                setTagArg(ds.getTagName(i), j, arg);
105
        }
106
    }
107

    
108
    // optimize storage
109
    parse(str());
110
}
111

    
112
void DisplayString::updateWith(const char *s)
113
{
114
    DisplayString ds(s);
115
    updateWith(ds);
116
}
117

    
118
bool DisplayString::containsTag(const char *tagname) const
119
{
120
    int t = gettagindex(tagname);
121
    return t!=-1;
122
}
123

    
124

    
125
int DisplayString::getNumArgs(const char *tagname) const
126
{
127
    return getNumArgs(gettagindex(tagname));
128
}
129

    
130

    
131
const char *DisplayString::getTagArg(const char *tagname, int index) const
132
{
133
    return getTagArg(gettagindex(tagname), index);
134
}
135

    
136

    
137
bool DisplayString::setTagArg(const char *tagname, int index, const char *value)
138
{
139
    int t = gettagindex(tagname);
140
    if (t==-1)
141
        t = insertTag(tagname);
142
    return setTagArg(t, index, value);
143
}
144

    
145
bool DisplayString::setTagArg(const char *tagname, int index, long value)
146
{
147
    char buf[32];
148
    sprintf(buf, "%ld", value);
149
    return setTagArg(tagname, index, buf);
150
}
151

    
152
bool DisplayString::removeTag(const char *tagname)
153
{
154
    return removeTag(gettagindex(tagname));
155
}
156

    
157

    
158
int DisplayString::getNumTags() const
159
{
160
    return numtags;
161
}
162

    
163

    
164
const char *DisplayString::getTagName(int tagindex) const
165
{
166
    if (tagindex<0 || tagindex>=numtags) return NULL;
167
    return tags[tagindex].name;
168
}
169

    
170

    
171
int DisplayString::getNumArgs(int tagindex) const
172
{
173
    if (tagindex<0 || tagindex>=numtags) return -1;
174
    return tags[tagindex].numargs;
175
}
176

    
177

    
178
const char *DisplayString::getTagArg(int tagindex, int index) const
179
{
180
    if (tagindex<0 || tagindex>=numtags) return "";
181
    if (index<0 || index>=tags[tagindex].numargs) return "";
182
    return opp_nulltoempty(tags[tagindex].args[index]);
183
}
184

    
185

    
186
bool DisplayString::setTagArg(int tagindex, int index, const char *value)
187
{
188
    // check indices
189
    if (tagindex<0 || tagindex>=numtags) return false;
190
    if (index<0 || index>=MAXARGS) return false;
191
    Tag& tag = tags[tagindex];
192

    
193
    // adjust numargs if necessary
194
    if (index>=tag.numargs)
195
        tag.numargs = index+1;
196

    
197
    // if it's the same, nothing to do
198
    char *&slot = tag.args[index];
199
    if (!opp_strcmp(slot,value))
200
        return true;
201

    
202
    // set value
203
    if (slot && !isinbuffer(slot))
204
        delete [] slot;
205
    slot = opp_strdup(value);
206

    
207
    // get rid of possible empty trailing args
208
    while (tag.numargs>0 && tag.args[tag.numargs-1]==NULL)
209
        tag.numargs--;
210

    
211
    needsassemble = true;
212
    return true;
213
}
214

    
215

    
216
int DisplayString::insertTag(const char *tagname, int atindex)
217
{
218
    // check uniqueness
219
    int t = gettagindex(tagname);
220
    if (t!=-1)
221
        return t;
222

    
223
    // check index
224
    if (atindex<0) atindex=0;
225
    if (atindex>numtags) atindex=numtags;
226

    
227
    // create new tags[] array with hole at atindex
228
    Tag *newtags = new Tag[numtags+1];
229
    for (int s=0,d=0; s<numtags; s++,d++)
230
    {
231
       if (d==atindex) d++; // make room for new item
232
       newtags[d] = tags[s];
233
    }
234
    delete [] tags;
235
    tags = newtags;
236
    numtags++;
237

    
238
    // fill in new tag
239
    tags[atindex].name = opp_strdup(tagname);
240
    tags[atindex].numargs = 0;
241
    for (int i=0; i<MAXARGS; i++) tags[atindex].args[i] = NULL;
242

    
243
    // success
244
    needsassemble = true;
245
    return atindex;
246
}
247

    
248

    
249
bool DisplayString::removeTag(int tagindex)
250
{
251
    if (tagindex<0 || tagindex>=numtags) return false;
252

    
253
    // dealloc strings in tag
254
    if (!isinbuffer(tags[tagindex].name))
255
        delete [] tags[tagindex].name;
256
    for (int i=0; i<tags[tagindex].numargs; i++)
257
        if (!isinbuffer(tags[tagindex].args[i]))
258
            delete [] tags[tagindex].args[i];
259

    
260
    // eliminate hole in tags[] array
261
    for (int t=tagindex; t<numtags-1; t++)
262
        tags[t] = tags[t+1];
263
    numtags--;
264

    
265
    // success
266
    needsassemble = true;
267
    return true;
268
}
269

    
270

    
271
int DisplayString::gettagindex(const char *tagname) const
272
{
273
    for (int t=0; t<numtags; t++)
274
        if (!strcmp(tagname,tags[t].name))
275
            return t;
276
    return -1;
277
}
278

    
279
void DisplayString::cleartags()
280
{
281
    // delete tags array. string pointers that do not point inside the
282
    // buffer were allocated individually via new char[] and have to be
283
    // deleted.
284
    for (int t=0; t<numtags; t++)
285
    {
286
        if (!isinbuffer(tags[t].name))
287
            delete [] tags[t].name;
288
        for (int i=0; i<tags[t].numargs; i++)
289
            if (!isinbuffer(tags[t].args[i]))
290
                delete [] tags[t].args[i];
291
    }
292
    delete [] tags;
293
    tags = NULL;
294
    numtags = 0;
295

    
296
    // must be done after deleting tags[] because of isinbuffer()
297
    delete [] buffer;
298
    buffer = bufferend = NULL;
299
    needsassemble = true;
300
}
301

    
302
bool DisplayString::parse()
303
{
304
    cleartags();
305
    if (dispstr==NULL)
306
        return true;
307

    
308
    bool fully_ok = true;
309

    
310
    buffer = new char[opp_strlen(dispstr)+1];
311
    bufferend = buffer + opp_strlen(dispstr);
312

    
313
    // count tags (#(';')+1) & allocate tags[] array
314
    int n = 1;
315
    for (char *s1 = dispstr; *s1; s1++)
316
        if (*s1==';')
317
            n++;
318
    tags = new Tag[n];
319

    
320
    // parse string into tags[]. To avoid several small allocations,
321
    // pointers in tags[] will point into the buffer.
322
    numtags = 1;
323
    tags[0].name = buffer;
324
    tags[0].numargs = 0;
325
    for (int i=0; i<MAXARGS; i++)
326
        tags[0].args[i] = NULL;
327

    
328
    char *s, *d;
329
    for (s=dispstr,d=buffer; *s; s++,d++)
330
    {
331
        if (*s=='\\' && *(s+1))
332
        {
333
            // allow escaping display string special chars (=,;) with backslash.
334
            // No need to deal with "\t", "\n" etc here, since they already got
335
            // interpreted by opp_parsequotedstr().
336
            *d = *++s;
337
        }
338
        else if (*s==';')
339
        {
340
            // new tag begins
341
            *d = '\0';
342
            numtags++;
343
            tags[numtags-1].name = d+1;
344
            tags[numtags-1].numargs = 0;
345
            for (int i=0; i<MAXARGS; i++) tags[numtags-1].args[i] = NULL;
346
        }
347
        else if (*s=='=')
348
        {
349
            // first argument of new tag begins
350
            *d = '\0';
351
            tags[numtags-1].numargs = 1;
352
            tags[numtags-1].args[0] = d+1;
353
        }
354
        else if (*s==',')
355
        {
356
            // new argument of current tag begins
357
            *d = '\0';
358
            if (tags[numtags-1].numargs>=MAXARGS)
359
            {
360
                fully_ok = false; // extra args ignored (there were too many)
361
            }
362
            else
363
            {
364
                tags[numtags-1].numargs++;
365
                tags[numtags-1].args[ tags[numtags-1].numargs-1 ] = d+1;
366
            }
367
        }
368
        else
369
        {
370
            *d = *s;
371
        }
372
    }
373
    *d = '\0';
374

    
375
    // check tag names are OK (matching [a-zA-Z0-9:]+)
376
    for (int i=0; i<numtags; i++)
377
    {
378
        if (!tags[i].name[0])
379
            fully_ok = false; // empty string as tagname
380
        for (const char *s=tags[i].name; *s; s++)
381
            if (!opp_isalnum(*s) && *s!=':')
382
                fully_ok = false; // tagname contains invalid character
383
    }
384
    return fully_ok;
385
}
386

    
387
void DisplayString::assemble() const
388
{
389
    // calculate length of display string
390
    int size = 0;
391
    for (int t0=0; t0<numtags; t0++)
392
    {
393
        size += opp_strlen(tags[t0].name)+2;
394
        for (int i=0; i<tags[t0].numargs; i++)
395
            size += opp_strlen(tags[t0].args[i])+1;
396
    }
397
    size = 2*size+1;  // 2* for worst case if every char has to be escaped
398

    
399
    // allocate display string
400
    delete [] dispstr;
401
    dispstr = new char[size];
402
    dispstr[0] = '\0';
403

    
404
    // assemble string
405
    for (int t=0; t<numtags; t++)
406
    {
407
        if (t!=0)
408
            strcat(dispstr, ";");
409
        strcatescaped(dispstr, tags[t].name);
410
        if (tags[t].numargs>0)
411
            strcat(dispstr, "=");
412
        for (int i=0; i<tags[t].numargs; i++)
413
        {
414
            if (i!=0) strcat(dispstr, ",");
415
            strcatescaped(dispstr, tags[t].args[i]);
416
        }
417
    }
418
    needsassemble = false;
419
}
420

    
421
void DisplayString::strcatescaped(char *d, const char *s)
422
{
423
    if (!s) return;
424

    
425
    d += strlen(d);
426
    while (*s)
427
    {
428
        // quoting \t, \n etc is the job of opp_quotestr()
429
        if (*s==';' || *s==',' || *s=='=')
430
            *d++ = '\\';
431
        *d++ = *s++;
432
    }
433
    *d = '\0';
434
}
435

    
436