~ubuntu-branches/debian/stretch/assaultcube-data/stretch

« back to all changes in this revision

Viewing changes to source/src/docs.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Gonéri Le Bouder, Ansgar Burchardt, Gonéri Le Bouder
  • Date: 2010-04-02 23:37:55 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100402233755-kf74fxwlu634o6vg
Tags: 1.0.4+repack1-1
[ Ansgar Burchardt ]
* debian/control: fix typo in short description

[ Gonéri Le Bouder ]
* Upgrade to 1.0.4
* bump standards-version to 3.8.4
* Add Depends: ${misc:Depends} just to avoid a lintian warning
* Add a debian/source/format file for the same reason

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// docs.cpp: ingame command documentation system
 
2
 
 
3
#include "pch.h"
 
4
#include "cube.h"
 
5
 
 
6
void renderdocsection(void *menu, bool init);
 
7
 
 
8
extern hashtable<const char *, ident> *idents;
 
9
 
 
10
struct docargument
 
11
{
 
12
    char *token, *desc, *values;
 
13
    bool vararg;
 
14
 
 
15
    docargument() : token(NULL), desc(NULL), values(NULL) {};
 
16
    ~docargument()
 
17
    {
 
18
        DELETEA(token);
 
19
        DELETEA(desc);
 
20
        DELETEA(values);
 
21
    }
 
22
};
 
23
 
 
24
struct docref
 
25
{
 
26
    char *name, *ident, *url, *article;
 
27
 
 
28
    docref() : name(NULL), ident(NULL), url(NULL), article(NULL) {}
 
29
    ~docref()
 
30
    {
 
31
        DELETEA(name);
 
32
        DELETEA(ident);
 
33
        DELETEA(url);
 
34
        DELETEA(article);
 
35
    }
 
36
};
 
37
 
 
38
struct docexample
 
39
{
 
40
    char *code, *explanation;
 
41
 
 
42
    docexample() : code(NULL), explanation(NULL) {}
 
43
    ~docexample()
 
44
    {
 
45
        DELETEA(code);
 
46
        DELETEA(explanation);
 
47
    }
 
48
};
 
49
 
 
50
struct dockey
 
51
{
 
52
    char *alias, *name, *desc;
 
53
 
 
54
    dockey() : alias(NULL), name(NULL), desc(NULL) {}
 
55
    ~dockey()
 
56
    {
 
57
        DELETEA(alias);
 
58
        DELETEA(name);
 
59
        DELETEA(desc);
 
60
    }
 
61
};
 
62
 
 
63
struct docident
 
64
{
 
65
    char *name, *desc;
 
66
    vector<docargument> arguments;
 
67
    cvector remarks;
 
68
    vector<docref> references;
 
69
    vector<docexample> examples;
 
70
    vector<dockey> keys;
 
71
 
 
72
    docident() : name(NULL), desc(NULL) {}
 
73
    ~docident()
 
74
    {
 
75
        DELETEA(name);
 
76
        DELETEA(desc);
 
77
    }
 
78
};
 
79
 
 
80
struct docsection
 
81
{
 
82
    char *name;
 
83
    vector<docident *> idents;
 
84
    void *menu;
 
85
 
 
86
    docsection() : name(NULL) {};
 
87
    ~docsection()
 
88
    {
 
89
        DELETEA(name);
 
90
    }
 
91
};
 
92
 
 
93
vector<docsection> sections;
 
94
hashtable<const char *, docident> docidents; // manage globally instead of a section tree to ensure uniqueness
 
95
docsection *lastsection = NULL;
 
96
docident *lastident = NULL;
 
97
 
 
98
void adddocsection(char *name)
 
99
{
 
100
    if(!name) return;
 
101
    docsection &s = sections.add();
 
102
    s.name = newstring(name);
 
103
    s.menu = addmenu(s.name, NULL, true, renderdocsection);
 
104
    lastsection = &s;
 
105
}
 
106
 
 
107
void adddocident(char *name, char *desc)
 
108
{
 
109
    if(!name || !desc || !lastsection) return;
 
110
    name = newstring(name);
 
111
    docident &c = docidents[name];
 
112
    lastsection->idents.add(&c);
 
113
    c.name = name;
 
114
    c.desc = newstring(desc);
 
115
    lastident = &c;
 
116
}
 
117
 
 
118
void adddocargument(char *token, char *desc, char *values, char *vararg)
 
119
{
 
120
    if(!lastident || !token || !desc) return;
 
121
    docargument &a = lastident->arguments.add();
 
122
    a.token = newstring(token);
 
123
    a.desc = newstring(desc);
 
124
    a.values = values && strlen(values) ? newstring(values) : NULL;
 
125
    a.vararg = vararg && atoi(vararg) == 1 ? true : false;
 
126
}
 
127
 
 
128
void adddocremark(char *remark)
 
129
{
 
130
    if(!lastident || !remark) return;
 
131
    lastident->remarks.add(newstring(remark));
 
132
}
 
133
 
 
134
void adddocref(char *name, char *ident, char *url, char *article)
 
135
{
 
136
    if(!lastident || !name) return;
 
137
    docref &r = lastident->references.add();
 
138
    r.name = newstring(name);
 
139
    r.ident = ident && strlen(ident) ? newstring(ident) : NULL;
 
140
    r.url = url && strlen(url) ? newstring(url) : NULL;
 
141
    r.article = article && strlen(article) ? newstring(article) : NULL;
 
142
}
 
143
 
 
144
void adddocexample(char *code, char *explanation)
 
145
{
 
146
    if(!lastident || !code) return;
 
147
    docexample &e = lastident->examples.add();
 
148
    e.code = newstring(code);
 
149
    e.explanation = explanation && strlen(explanation) ? newstring(explanation) : NULL;
 
150
}
 
151
 
 
152
void adddockey(char *alias, char *name, char *desc)
 
153
{
 
154
    if(!lastident || !alias) return;
 
155
    dockey &k = lastident->keys.add();
 
156
    k.alias = newstring(alias);
 
157
    k.name = name && strlen(name) ? newstring(name) : NULL;
 
158
    k.desc = desc && strlen(desc) ? newstring(desc) : NULL;
 
159
}
 
160
 
 
161
COMMANDN(docsection, adddocsection, ARG_1STR);
 
162
COMMANDN(docident, adddocident, ARG_2STR);
 
163
COMMANDN(docargument, adddocargument, ARG_4STR);
 
164
COMMANDN(docremark, adddocremark, ARG_1STR);
 
165
COMMANDN(docref, adddocref, ARG_3STR);
 
166
COMMANDN(docexample, adddocexample, ARG_2STR);
 
167
COMMANDN(dockey, adddockey, ARG_3STR);
 
168
 
 
169
int stringsort(const char **a, const char **b) { return strcmp(*a, *b); }
 
170
 
 
171
char *cvecstr(vector<char *> &cvec, const char *substr, int *rline = NULL)
 
172
{
 
173
    char *r = NULL;
 
174
    loopv(cvec) if(cvec[i]) if((r = strstr(cvec[i], substr)) != NULL)
 
175
    {
 
176
        if(rline) *rline = i;
 
177
        break;
 
178
    }
 
179
    return r;
 
180
}
 
181
 
 
182
void docundone(int allidents)
 
183
{
 
184
    vector<const char *> inames;
 
185
    identnames(inames, !(allidents > 0));
 
186
    inames.sort(stringsort);
 
187
    loopv(inames)
 
188
    {
 
189
        docident *id = docidents.access(inames[i]);
 
190
        if(id) // search for substrings that indicate undoneness
 
191
        {
 
192
            cvector srch;
 
193
            srch.add(id->name);
 
194
            srch.add(id->desc);
 
195
            loopvj(id->remarks) srch.add(id->remarks[j]);
 
196
            loopvj(id->arguments)
 
197
            {
 
198
                srch.add(id->arguments[j].token);
 
199
                srch.add(id->arguments[j].desc);
 
200
                srch.add(id->arguments[j].values);
 
201
            }
 
202
            loopvj(id->references)
 
203
            {
 
204
                srch.add(id->references[j].ident);
 
205
                srch.add(id->references[j].name);
 
206
                srch.add(id->references[j].url);
 
207
            }
 
208
            if(!cvecstr(srch, "TODO") && !cvecstr(srch, "UNDONE")) continue;
 
209
        }
 
210
        conoutf("%s", inames[i]);
 
211
    }
 
212
}
 
213
 
 
214
void docinvalid()
 
215
{
 
216
    vector<const char *> inames;
 
217
    identnames(inames, true);
 
218
    inames.sort(stringsort);
 
219
    enumerate(docidents, docident, d,
 
220
    {
 
221
        if(!strchr(d.name, ' ') && !identexists(d.name))
 
222
            conoutf("%s", d.name);
 
223
    });
 
224
}
 
225
 
 
226
void docfind(char *search)
 
227
{
 
228
    enumerate(docidents, docident, i,
 
229
    {
 
230
        cvector srch;
 
231
        srch.add(i.name);
 
232
        srch.add(i.desc);
 
233
        loopvk(i.remarks) srch.add(i.remarks[k]);
 
234
 
 
235
        char *r;
 
236
        int rline;
 
237
        if((r = cvecstr(srch, search, &rline)))
 
238
        {
 
239
            const int matchchars = 200;
 
240
            string match;
 
241
            s_strncpy(match, r-srch[rline] > matchchars/2 ? r-matchchars/2 : srch[rline], matchchars/2);
 
242
            conoutf("%-20s%s", i.name, match);
 
243
        }
 
244
    });
 
245
}
 
246
 
 
247
char *xmlstringenc(char *d, const char *s, size_t len)
 
248
{
 
249
    if(!d || !s) return NULL;
 
250
    struct spchar { char c; char repl[8]; } const spchars[] = { {'&', "&amp;"}, {'<', "&lt;"}, {'>', "gt;"}, {'"', "&quot;"}, {'\'', "&apos;"}};
 
251
 
 
252
    char *dc = d;
 
253
    const char *sc = s;
 
254
 
 
255
    while(*sc && (size_t)(dc - d) < len - 1)
 
256
    {
 
257
        bool specialc = false;
 
258
        loopi(sizeof(spchars)/sizeof(spchar)) if(spchars[i].c == *sc)
 
259
        {
 
260
            specialc = true;
 
261
            size_t rlen = strlen(spchars[i].repl);
 
262
            if(dc - d + rlen <= len - 1)
 
263
            {
 
264
                memcpy(dc, spchars[i].repl, rlen);
 
265
                dc += rlen;
 
266
                break;
 
267
            }
 
268
        }
 
269
        if(!specialc) memcpy(dc++, sc, 1);
 
270
        *dc = 0;
 
271
        sc++;
 
272
    }
 
273
    return d;
 
274
}
 
275
 
 
276
void docwritebaseref(char *ref, char *schemalocation, char *transformation)
 
277
{
 
278
    FILE *f = openfile(path("docs/autogenerated_base_reference.xml", true), "w");
 
279
    if(!f) return;
 
280
    char desc[] = "<description>TODO: Description</description>";
 
281
 
 
282
    fprintf(f, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
 
283
    fprintf(f, "<?xml-stylesheet type=\"text/xsl\" href=\"%s\"?>\n", transformation && strlen(transformation) ? transformation : "transformations/cuberef2xhtml.xslt");
 
284
    fprintf(f, "<cuberef name=\"%s\" version=\"v0.1\" xsi:schemaLocation=\"%s\" xmlns=\"http://cubers.net/Schemas/CubeRef\">\n", ref && strlen(ref) ? ref : "Unnamed Reference", schemalocation && strlen(schemalocation) ? schemalocation : "http://cubers.net/Schemas/CubeRef schemas/cuberef.xsd");
 
285
    fprintf(f, "\t%s\n", desc);
 
286
    fprintf(f, "\t<sections>\n");
 
287
    fprintf(f, "\t\t<section name=\"Main\">\n");
 
288
    fprintf(f, "\t\t\t%s\n", desc);
 
289
    fprintf(f, "\t\t\t<identifiers>\n");
 
290
 
 
291
    static const int bases[] = { ARG_1INT, ARG_1STR, ARG_1EXP, ARG_1EXPF, ARG_1EST };
 
292
    string name;
 
293
    enumerate(*idents, ident, id,
 
294
    {
 
295
        if(id.type != ID_COMMAND) continue;
 
296
        fprintf(f, "\t\t\t\t<command name=\"%s\">\n", xmlstringenc(name, id.name, _MAXDEFSTR));
 
297
        fprintf(f, "\t\t\t\t\t%s\n", desc);
 
298
        if(id.narg != ARG_NONE && id.narg != ARG_DOWN && id.narg != ARG_IVAL && id.narg != ARG_FVAL && id.narg != ARG_SVAL)
 
299
        {
 
300
            fprintf(f, "\t\t\t\t\t<arguments>\n");
 
301
            if(id.narg == ARG_CONC || id.narg == ARG_CONCW || id.narg == ARG_VARI) fprintf(f, "\t\t\t\t\t\t<variableArgument token=\"...\" description=\"TODO\"/>\n");
 
302
            else
 
303
            {
 
304
                int base = -1;
 
305
                loopj(sizeof(bases)/sizeof(bases[0]))
 
306
                {
 
307
                    if(id.narg < bases[j]) { if(j) base = bases[j-1]; break; }
 
308
                }
 
309
                if(base >= 0) loopj(id.narg-base+1) fprintf(f, "\t\t\t\t\t\t<argument token=\"%c\" description=\"TODO\"/>\n", (char)(*"A")+j);
 
310
            }
 
311
            fprintf(f, "\t\t\t\t\t</arguments>\n");
 
312
        }
 
313
        fprintf(f, "\t\t\t\t</command>\n");
 
314
    });
 
315
    enumerate(*idents, ident, id,
 
316
    {
 
317
        if(id.type != ID_VAR && id.type != ID_FVAR) continue;
 
318
        fprintf(f, "\t\t\t\t<variable name=\"%s\">\n", xmlstringenc(name, id.name, _MAXDEFSTR));
 
319
        fprintf(f, "\t\t\t\t\t<description>TODO</description>\n");
 
320
        switch(id.type)
 
321
        {
 
322
            case ID_VAR:
 
323
                fprintf(f, "\t\t\t\t\t<value %s description=\"TODO\" minValue=\"%i\" maxValue=\"%i\" defaultValue=\"%i\" %s/>\n", id.minval>id.maxval ? "" : "token=\"N\"", id.minval, id.maxval, *id.storage.i, id.minval>id.maxval ? "readOnly=\"true\"" : "");
 
324
                break;
 
325
            case ID_FVAR:
 
326
                fprintf(f, "\t\t\t\t\t<value %s description=\"TODO\" minValue=\"%s\" maxValue=\"%s\" defaultValue=\"%s\" %s/>\n", id.minvalf>id.maxvalf ? "" : "token=\"N\"",
 
327
                    floatstr(id.minvalf), floatstr(id.maxvalf), floatstr(*id.storage.f),
 
328
                    id.minvalf>id.maxvalf ? "readOnly=\"true\"" : "");
 
329
                break;
 
330
        }
 
331
        fprintf(f, "\t\t\t\t</variable>\n");
 
332
    });
 
333
 
 
334
    fprintf(f, "\t\t\t</identifiers>\n\t\t</section>\n\t</sections>\n</cuberef>\n");
 
335
    fclose(f);
 
336
}
 
337
 
 
338
COMMAND(docundone, ARG_1INT);
 
339
COMMAND(docinvalid, ARG_NONE);
 
340
COMMAND(docfind, ARG_1STR);
 
341
COMMAND(docwritebaseref, ARG_3STR);
 
342
VAR(docvisible, 0, 1, 1);
 
343
VAR(docskip, 0, 0, 1000);
 
344
 
 
345
void toggledoc() { docvisible = !docvisible; }
 
346
void scrolldoc(int i) { docskip += i; if(docskip < 0) docskip = 0; }
 
347
 
 
348
int numargs(char *args)
 
349
{
 
350
    if(!args || !strlen(args)) return -1;
 
351
 
 
352
    int argidx = -1;
 
353
    char *argstart = NULL;
 
354
 
 
355
    for(char *t = args; *t; t++)
 
356
    {
 
357
        if(!argstart && *t != ' ') { argstart = t; argidx++; }
 
358
        else if(argstart && *t == ' ') if(t-1 >= args)
 
359
        {
 
360
            switch(*argstart)
 
361
            {
 
362
                case '[': if(*(t-1) != ']') continue; break;
 
363
                case '"': if(*(t-1) != '"') continue; break;
 
364
                default: break;
 
365
            }
 
366
            argstart = NULL;
 
367
        }
 
368
    }
 
369
    return argidx;
 
370
}
 
371
 
 
372
void renderdoc(int x, int y, int doch)
 
373
{
 
374
    if(!docvisible) return;
 
375
 
 
376
    char *exp = getcurcommand();
 
377
    if(!exp || *exp != '/' || strlen(exp) < 2) return;
 
378
 
 
379
    char *c = exp+1;
 
380
    size_t clen = strlen(c);
 
381
    for(size_t i = 0; i < clen; i++) // search first matching cmd doc by stripping arguments of exp from right to left
 
382
    {
 
383
        char *end = c+clen-i;
 
384
        if(!*end || *end == ' ')
 
385
        {
 
386
            string cmd;
 
387
            s_strncpy(cmd, c, clen-i+1);
 
388
            docident *ident = docidents.access(cmd);
 
389
            if(ident)
 
390
            {
 
391
                vector<const char *> doclines;
 
392
 
 
393
                char *label = newstringbuf(); // label
 
394
                doclines.add(label);
 
395
                s_sprintf(label)("~%s", ident->name);
 
396
                loopvj(ident->arguments)
 
397
                {
 
398
                    s_strcat(label, " ");
 
399
                    s_strcat(label, ident->arguments[j].token);
 
400
                }
 
401
                doclines.add(NULL);
 
402
 
 
403
                doclines.add(ident->desc);
 
404
                doclines.add(NULL);
 
405
 
 
406
                if(ident->arguments.length() > 0) // args
 
407
                {
 
408
                    extern textinputbuffer cmdline;
 
409
                    char *args = strchr(c, ' ');
 
410
                    int arg = -1;
 
411
 
 
412
                    if(args)
 
413
                    {
 
414
                        args++;
 
415
                        if(cmdline.pos >= 0)
 
416
                        {
 
417
                            if(cmdline.pos >= args-c)
 
418
                            {
 
419
                                string a;
 
420
                                s_strncpy(a, args, cmdline.pos-(args-c)+1);
 
421
                                args = a;
 
422
                                arg = numargs(args);
 
423
                            }
 
424
                        }
 
425
                        else arg = numargs(args);
 
426
 
 
427
                        if(arg >= 0) // multipart idents need a fixed argument offset
 
428
                        {
 
429
                            char *c = cmd;
 
430
                            while((c = strchr(c, ' ')) && c++) arg--;
 
431
                        }
 
432
 
 
433
                        // fixes offset for var args
 
434
                        if(arg >= ident->arguments.length() && ident->arguments.last().vararg) arg = ident->arguments.length() - 1;
 
435
                    }
 
436
 
 
437
                    loopvj(ident->arguments)
 
438
                    {
 
439
                        docargument *a = &ident->arguments[j];
 
440
                        if(!a) continue;
 
441
                        s_sprintf(doclines.add(newstringbuf()))("~\f%d%-8s%s %s%s%s", j == arg ? 4 : 5, a->token, a->desc,
 
442
                            a->values ? "(" : "", a->values ? a->values : "", a->values ? ")" : "");
 
443
                    }
 
444
 
 
445
                    doclines.add(NULL);
 
446
                }
 
447
 
 
448
                if(ident->remarks.length()) // remarks
 
449
                {
 
450
                    loopvj(ident->remarks) doclines.add(ident->remarks[j]);
 
451
                    doclines.add(NULL);
 
452
                }
 
453
 
 
454
                if(ident->examples.length()) // examples
 
455
                {
 
456
                    doclines.add(ident->examples.length() == 1 ? "Example:" : "Examples:");
 
457
                    loopvj(ident->examples)
 
458
                    {
 
459
                        doclines.add(ident->examples[j].code);
 
460
                        doclines.add(ident->examples[j].explanation);
 
461
                    }
 
462
                    doclines.add(NULL);
 
463
                }
 
464
 
 
465
                if(ident->keys.length()) // default keys
 
466
                {
 
467
                    doclines.add(ident->keys.length() == 1 ? "Default key:" : "Default keys:");
 
468
                    loopvj(ident->keys)
 
469
                    {
 
470
                        dockey &k = ident->keys[j];
 
471
                        s_sprintfd(line)("~%-10s %s", k.name ? k.name : k.alias, k.desc ? k.desc : "");
 
472
                        doclines.add(newstring(line));
 
473
                    }
 
474
                    doclines.add(NULL);
 
475
                }
 
476
 
 
477
                if(ident->references.length()) // references
 
478
                {
 
479
                    struct category { string label; string refs; }
 
480
                    categories[] = {{"related identifiers", ""} , {"web resources", ""}, {"wiki articles", ""}, {"other", ""}};
 
481
                    loopvj(ident->references)
 
482
                    {
 
483
                        docref &r = ident->references[j];
 
484
                        char *ref = r.ident ? categories[0].refs : (r.url ? categories[1].refs : (r.article ? categories[2].refs : categories[3].refs));
 
485
                        s_strcat(ref, r.name);
 
486
                        if(j < ident->references.length()-1) s_strcat(ref, ", ");
 
487
                    }
 
488
                    loopj(sizeof(categories)/sizeof(category))
 
489
                    {
 
490
                        if(!strlen(categories[j].refs)) continue;
 
491
                        s_sprintf(doclines.add(newstringbuf()))("~%s: %s", categories[j].label, categories[j].refs);
 
492
                    }
 
493
                }
 
494
 
 
495
                while(doclines.length() && !doclines.last()) doclines.pop();
 
496
 
 
497
                doch -= 3*FONTH + FONTH/2;
 
498
 
 
499
                int offset = min(docskip, doclines.length()-1), maxl = offset, cury = 0;
 
500
                for(int j = offset; j < doclines.length(); j++)
 
501
                {
 
502
                    const char *str = doclines[j];
 
503
                    int width = 0, height = FONTH;
 
504
                    if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3);
 
505
                    if(cury + height > doch) break;
 
506
                    cury += height;
 
507
                    maxl = j+1;
 
508
                }
 
509
                if(offset > 0 && maxl >= doclines.length())
 
510
                {
 
511
                    for(int j = offset-1; j >= 0; j--)
 
512
                    {
 
513
                        const char *str = doclines[j];
 
514
                        int width = 0, height = FONTH;
 
515
                        if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3);
 
516
                        if(cury + height > doch) break;
 
517
                        cury += height;
 
518
                        offset = j;
 
519
                    }
 
520
                }
 
521
 
 
522
 
 
523
                cury = y;
 
524
                for(int j = offset; j < maxl; j++)
 
525
                {
 
526
                    const char *str = doclines[j];
 
527
                    if(str)
 
528
                    {
 
529
                        const char *text = *str=='~' ? str+1 : str;
 
530
                        draw_text(text, x, cury, 0xFF, 0xFF, 0xFF, 0xFF, -1, str==text ? VIRTW*4/3 : -1);
 
531
                        int width, height;
 
532
                        text_bounds(text, width, height, str==text ? VIRTW*4/3 : -1);
 
533
                        cury += height;
 
534
                        if(str!=text) delete[] str;
 
535
                    }
 
536
                    else cury += FONTH;
 
537
                }
 
538
 
 
539
                if(maxl < doclines.length()) draw_text("\f4more (F3)", x, y+doch); // footer
 
540
                if(offset > 0) draw_text("\f4less (F2)", x, y+doch+FONTH);
 
541
                draw_text("\f4disable doc reference (F1)", x, y+doch+2*FONTH);
 
542
                return;
 
543
            }
 
544
        }
 
545
    }
 
546
}
 
547
 
 
548
void *docmenu = NULL;
 
549
 
 
550
struct msection { char *name; string cmd; };
 
551
 
 
552
int msectionsort(const msection *a, const msection *b)
 
553
{
 
554
    return strcmp(a->name, b->name);
 
555
}
 
556
 
 
557
void renderdocsection(void *menu, bool init)
 
558
{
 
559
    static vector<msection> msections;
 
560
    msections.setsize(0);
 
561
 
 
562
    loopv(sections)
 
563
    {
 
564
        if(sections[i].menu != menu) continue;
 
565
        loopvj(sections[i].idents)
 
566
        {
 
567
            docident &id = *sections[i].idents[j];
 
568
            msection &s = msections.add();
 
569
            s.name = id.name;
 
570
            s_sprintf(s.cmd)("saycommand [/%s ]", id.name);
 
571
        }
 
572
        msections.sort(msectionsort);
 
573
        menureset(menu);
 
574
        loopv(msections) { menumanual(menu, msections[i].name, msections[i].cmd); }
 
575
        return;
 
576
    }
 
577
}
 
578
 
 
579
struct maction { string cmd; };
 
580
 
 
581
void renderdocmenu(void *menu, bool init)
 
582
{
 
583
    static vector<maction> actions;
 
584
    actions.setsize(0);
 
585
    menureset(menu);
 
586
    loopv(sections)
 
587
    {
 
588
        maction &a = actions.add();
 
589
        s_sprintf(a.cmd)("showmenu [%s]", sections[i].name);
 
590
        menumanual(menu, sections[i].name, a.cmd);
 
591
    }
 
592
}