Fossil SCM

fossil-scm / tools / mkindex.c
Blame History Raw 608 lines
1
/*
2
** Copyright (c) 2002 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
**
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This utility program scans Fossil source text looking for specially
19
** formatted comments and generates C source code for constant tables
20
** that define the behavior of commands, webpages, and settings.
21
**
22
** USAGE:
23
**
24
** mkindex *.c >page_index.h
25
**
26
** Run this command with arguments that are all input source files to
27
** scan. Generated C code appears on standard output. The generated
28
** C code includes structures that:
29
**
30
** * Map command names to the C-language functions that implement
31
** those command.
32
**
33
** * Map webpage names to the C-language functions that implement
34
** those web pages.
35
**
36
** * Map settings into attributes, such as they default value for
37
** each setting, and the kind of value (boolean, multi-line, etc).
38
**
39
** * Provide help text for commands, webpages, settings, and other
40
** miscellanous help topics.
41
**
42
** COMMENT TEXT THAT THIS PROGRAM LOOKS FOR:
43
**
44
** The source code is scanned for comment lines of the form:
45
**
46
** WEBPAGE: /abc/xyz
47
** COMMAND: cmdname
48
** SETTING: access-log
49
** TOPIC: help-topic
50
**
51
** The WEBPAGE and COMMAND comments should be followed by a function that
52
** implements the webpage or command. The form of this function is:
53
**
54
** void function_name(void){
55
**
56
** Command names can divided into three classes: 1st-tier, 2nd-tier,
57
** and test. 1st-tier commands are the most frequently used and the
58
** ones that show up with "fossil help". 2nd-tier are seldom-used and/or
59
** legacy commands. Test commands are unsupported commands used for testing
60
** and analysis only.
61
**
62
** Commands are 1st-tier by default. If the command name begins with
63
** "test-" or if the command name has a "test" argument, then it becomes
64
** a test command. If the command name has a "2nd-tier" argument or ends
65
** with a "*" character, it is second tier. If the command name has an "alias"
66
** argument or ends with a "#" character, it is an alias: another name
67
** (a one-to-one replacement) for a command. Examples:
68
**
69
** COMMAND: abcde*
70
** COMMAND: fghij 2nd-tier
71
** COMMAND: mnopq#
72
** COMMAND: rstuv alias
73
** COMMAND: test-xyzzy
74
** COMMAND: xyzzy test
75
**
76
** A SETTING: may be followed by arguments that give additional attributes
77
** to that setting:
78
**
79
** SETTING: clean-blob versionable width=40 block-text
80
** SETTING: auto-shun boolean default=on
81
**
82
** New arguments may be added in future releases that set additional
83
** bits in the eCmdFlags field.
84
**
85
** Additional lines of comment after the COMMAND: or WEBPAGE: or SETTING:
86
** become the built-in help text for that command or webpage or setting.
87
** Backslashes must be escaped ("\\" in comment yields "\" in the help text.)
88
**
89
** Multiple COMMAND: entries can be attached to the same command, thus
90
** creating multiple aliases for that command. Similarly, multiple
91
** WEBPAGE: entries can be attached to the same webpage function, to give
92
** that page aliases.
93
**
94
** For SETTING: entries, the default value for the setting can be specified
95
** using a default=VALUE argument if the default contains no spaces. If the
96
** default value does contain spaces, use a separate line like this:
97
**
98
** SETTING: pgp-command
99
** DEFAULT: gpg --clearsign -o
100
**
101
** If no default is supplied, the default is assumed to be an empty string
102
** or "off" in the case of a boolean.
103
**
104
** A TOPIC: is followed by help text for the named topic.
105
**
106
** OUTPUTS:
107
**
108
** The output is C-language text to define and initialize a constant
109
** array of CmdOrPage objects named "aCommand[]". That array is a global
110
** variable. The dispatch.c source file defines the CmdOrPage object and
111
** deals with the aCommand[] global variable.
112
**
113
** The output also contains a constant array of Setting objects named
114
** aSetting[]. The Setting object is defined in db.c.
115
*/
116
#include <stdio.h>
117
#include <stdlib.h>
118
#include <assert.h>
119
#include <string.h>
120
121
/***************************************************************************
122
** These macros must match similar macros in dispatch.c.
123
**
124
** Allowed values for CmdOrPage.eCmdFlags. */
125
#define CMDFLAG_1ST_TIER 0x000001 /* Most important commands */
126
#define CMDFLAG_2ND_TIER 0x000002 /* Obscure and seldom used commands */
127
#define CMDFLAG_TEST 0x000004 /* Commands for testing only */
128
#define CMDFLAG_WEBPAGE 0x000008 /* Web pages */
129
#define CMDFLAG_COMMAND 0x000010 /* A command */
130
#define CMDFLAG_SETTING 0x000020 /* A setting */
131
#define CMDFLAG_VERSIONABLE 0x000040 /* A versionable setting */
132
#define CMDFLAG_BLOCKTEXT 0x000080 /* Multi-line text setting */
133
#define CMDFLAG_BOOLEAN 0x000100 /* A boolean setting */
134
#define CMDFLAG_RAWCONTENT 0x000200 /* Do not interpret webpage content */
135
#define CMDFLAG_SENSITIVE 0x000400 /* Security-sensitive setting */
136
#define CMDFLAG_HIDDEN 0x000800 /* Elide from most listings */
137
#define CMDFLAG_LDAVG_EXEMPT 0x001000 /* Exempt from load_control() */
138
#define CMDFLAG_ALIAS 0x002000 /* Command aliases */
139
#define CMDFLAG_KEEPEMPTY 0x004000 /* Do not unset empty settings */
140
#define CMDFLAG_ABBREVSUBCMD 0x008000 /* Abbreviated subcmd in help text */
141
#define CMDFLAG_TOPIC 0x010000 /* A help topic */
142
/**************************************************************************/
143
144
/*
145
** Each entry looks like this:
146
*/
147
typedef struct Entry {
148
int eType; /* CMDFLAG_* values */
149
char *zIf; /* Enclose in #if */
150
char *zFunc; /* Name of implementation */
151
char *zPath; /* Webpage or command name */
152
char *zHelp; /* Help text */
153
char *zDflt; /* Default value for settings */
154
char *zVar; /* config.name for settings, if different from zPath */
155
int iHelp; /* Index of Help text */
156
int iWidth; /* Display width for SETTING: values */
157
} Entry;
158
159
/*
160
** Maximum number of entries
161
*/
162
#define N_ENTRY 5000
163
164
/*
165
** Maximum size of a help message
166
*/
167
#define MX_HELP 250000
168
169
/*
170
** Table of entries
171
*/
172
Entry aEntry[N_ENTRY];
173
174
/*
175
** Current help message accumulator
176
*/
177
char zHelp[MX_HELP];
178
int nHelp;
179
180
/*
181
** Most recently encountered #if
182
*/
183
char zIf[2000];
184
185
/*
186
** How many entries are used
187
*/
188
int nUsed;
189
int nFixed;
190
191
/*
192
** Current filename and line number
193
*/
194
char *zFile;
195
int nLine;
196
197
/*
198
** Number of errors
199
*/
200
int nErr = 0;
201
202
/*
203
** Duplicate N characters of a string.
204
*/
205
char *string_dup(const char *zSrc, int n){
206
char *z;
207
if( n<0 ) n = strlen(zSrc);
208
z = malloc( n+1 );
209
if( z==0 ){ fprintf(stderr,"Out of memory!\n"); exit(1); }
210
strncpy(z, zSrc, n);
211
z[n] = 0;
212
return z;
213
}
214
215
/*
216
** Safe isspace macro. Works with signed characters.
217
*/
218
int fossil_isspace(char c){
219
return c==' ' || (c<='\r' && c>='\t');
220
}
221
222
/*
223
** Safe isident macro. Works with signed characters.
224
*/
225
int fossil_isident(char c){
226
if( c>='a' && c<='z' ) return 1;
227
if( c>='A' && c<='Z' ) return 1;
228
if( c>='0' && c<='9' ) return 1;
229
if( c=='_' ) return 1;
230
return 0;
231
}
232
233
/*
234
** Scan a line looking for comments containing zLabel. Make
235
** new entries if found.
236
*/
237
void scan_for_label(const char *zLabel, char *zLine, int eType){
238
int i, j;
239
int len = strlen(zLabel);
240
if( nUsed>=N_ENTRY ) return;
241
for(i=0; fossil_isspace(zLine[i]) || zLine[i]=='*'; i++){}
242
if( zLine[i]!=zLabel[0] ) return;
243
if( strncmp(&zLine[i],zLabel, len)==0 ){
244
i += len;
245
}else{
246
return;
247
}
248
while( fossil_isspace(zLine[i]) ){ i++; }
249
if( zLine[i]=='/' ) i++;
250
for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){}
251
aEntry[nUsed].eType = eType;
252
if( eType & CMDFLAG_WEBPAGE ){
253
aEntry[nUsed].zPath = string_dup(&zLine[i-1], j+1);
254
aEntry[nUsed].zPath[0] = '/';
255
}else{
256
aEntry[nUsed].zPath = string_dup(&zLine[i], j);
257
}
258
aEntry[nUsed].zFunc = 0;
259
if( (eType & CMDFLAG_COMMAND)!=0 ){
260
if( strncmp(&zLine[i], "test-", 5)==0 ){
261
/* Commands that start with "test-" are test-commands */
262
aEntry[nUsed].eType |= CMDFLAG_TEST;
263
}else if( zLine[i+j-1]=='*' ){
264
/* If the command name ends in '*', remove the '*' from the name
265
** but move the command into the second tier */
266
aEntry[nUsed].zPath[j-1] = 0;
267
aEntry[nUsed].eType |= CMDFLAG_2ND_TIER;
268
}else if( zLine[i+j-1]=='#' ){
269
/* If the command name ends in '#', remove the '#' from the name
270
** but move the command into aliases */
271
aEntry[nUsed].zPath[j-1] = 0;
272
aEntry[nUsed].eType |= CMDFLAG_ALIAS;
273
}else{
274
/* Otherwise, this is a first-tier command */
275
aEntry[nUsed].eType |= CMDFLAG_1ST_TIER;
276
}
277
}
278
279
/* Process additional flags that might follow the command name */
280
while( zLine[i+j]!=0 ){
281
i += j;
282
while( fossil_isspace(zLine[i]) ){ i++; }
283
if( zLine[i]==0 ) break;
284
for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){}
285
if( j==8 && strncmp(&zLine[i], "1st-tier", j)==0 ){
286
aEntry[nUsed].eType &= ~(CMDFLAG_2ND_TIER|CMDFLAG_TEST|CMDFLAG_ALIAS);
287
aEntry[nUsed].eType |= CMDFLAG_1ST_TIER;
288
}else if( j==8 && strncmp(&zLine[i], "2nd-tier", j)==0 ){
289
aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_TEST|CMDFLAG_ALIAS);
290
aEntry[nUsed].eType |= CMDFLAG_2ND_TIER;
291
}else if( j==4 && strncmp(&zLine[i], "test", j)==0 ){
292
aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_2ND_TIER|CMDFLAG_ALIAS);
293
aEntry[nUsed].eType |= CMDFLAG_TEST;
294
}else if( j==5 && strncmp(&zLine[i], "alias", j)==0 ){
295
aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_2ND_TIER|CMDFLAG_TEST);
296
aEntry[nUsed].eType |= CMDFLAG_ALIAS;
297
}else if( j==11 && strncmp(&zLine[i], "raw-content", j)==0 ){
298
aEntry[nUsed].eType |= CMDFLAG_RAWCONTENT;
299
}else if( j==7 && strncmp(&zLine[i], "boolean", j)==0 ){
300
aEntry[nUsed].eType &= ~(CMDFLAG_BLOCKTEXT);
301
aEntry[nUsed].iWidth = 0;
302
aEntry[nUsed].eType |= CMDFLAG_BOOLEAN;
303
}else if( j==10 && strncmp(&zLine[i], "block-text", j)==0 ){
304
aEntry[nUsed].eType &= ~(CMDFLAG_BOOLEAN);
305
aEntry[nUsed].eType |= CMDFLAG_BLOCKTEXT;
306
}else if( j==10 && strncmp(&zLine[i], "keep-empty", j)==0 ){
307
aEntry[nUsed].eType |= CMDFLAG_KEEPEMPTY;
308
}else if( j==11 && strncmp(&zLine[i], "versionable", j)==0 ){
309
aEntry[nUsed].eType |= CMDFLAG_VERSIONABLE;
310
}else if( j==9 && strncmp(&zLine[i], "sensitive", j)==0 ){
311
aEntry[nUsed].eType |= CMDFLAG_SENSITIVE;
312
}else if( j>6 && strncmp(&zLine[i], "width=", 6)==0 ){
313
aEntry[nUsed].iWidth = atoi(&zLine[i+6]);
314
}else if( j>8 && strncmp(&zLine[i], "default=", 8)==0 ){
315
aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8);
316
}else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){
317
aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
318
}else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
319
aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
320
}else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
321
aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT;
322
}else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0)
323
|| (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){
324
aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD;
325
}else{
326
fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
327
zFile, nLine, j, &zLine[i]);
328
nErr++;
329
}
330
}
331
332
nUsed++;
333
return;
334
}
335
336
/*
337
** Check to see if the current line is an #if and if it is, add it to
338
** the zIf[] string. If the current line is an #endif or #else or #elif
339
** then cancel the current zIf[] string.
340
*/
341
void scan_for_if(const char *zLine){
342
int i;
343
int len;
344
if( zLine[0]!='#' ) return;
345
for(i=1; fossil_isspace(zLine[i]); i++){}
346
if( zLine[i]==0 ) return;
347
len = strlen(&zLine[i]);
348
if( strncmp(&zLine[i],"if",2)==0 ){
349
zIf[0] = '#';
350
memcpy(&zIf[1], &zLine[i], len+1);
351
}else if( zLine[i]=='e' ){
352
zIf[0] = 0;
353
}
354
}
355
356
/*
357
** Check to see if the current line is a "** DEFAULT: ..." line for a
358
** SETTING definition. If so, remember the default value.
359
*/
360
void scan_for_default(const char *zLine){
361
int len;
362
const char *z;
363
if( nUsed<1 ) return;
364
if( (aEntry[nUsed-1].eType & CMDFLAG_SETTING)==0 ) return;
365
if( strncmp(zLine, "** DEFAULT: ", 12)!=0 ) return;
366
z = zLine + 12;
367
while( fossil_isspace(z[0]) ) z++;
368
len = (int)strlen(z);
369
while( len>0 && fossil_isspace(z[len-1]) ){ len--; }
370
aEntry[nUsed-1].zDflt = string_dup(z,len);
371
}
372
373
/* Local strcpy() clone to squelch an unwarranted warning from OpenBSD. */
374
static void local_strcpy(char *dest, const char *src){
375
while( (*(dest++) = *(src++))!=0 ){}
376
}
377
378
/*
379
** Scan a line for a function that implements a web page or command.
380
*/
381
void scan_for_func(char *zLine){
382
int i,j,k;
383
char *z;
384
int hasFunc;
385
if( nUsed<=nFixed ) return;
386
if( strncmp(zLine, "**", 2)==0
387
&& fossil_isspace(zLine[2])
388
&& strlen(zLine)<sizeof(zHelp)-nHelp-1
389
&& nUsed>nFixed
390
&& strncmp(zLine,"** COMMAND:",11)!=0
391
&& strncmp(zLine,"** WEBPAGE:",11)!=0
392
&& strncmp(zLine,"** SETTING:",11)!=0
393
&& strncmp(zLine,"** DEFAULT:",11)!=0
394
&& strncmp(zLine,"** TOPIC:",9)!=0
395
){
396
if( zLine[2]=='\n' ){
397
zHelp[nHelp++] = '\n';
398
}else{
399
if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0;
400
local_strcpy(&zHelp[nHelp], &zLine[3]);
401
nHelp += strlen(&zHelp[nHelp]);
402
}
403
return;
404
}
405
for(i=0; fossil_isspace(zLine[i]); i++){}
406
if( zLine[i]==0 ) return;
407
hasFunc = (aEntry[nFixed].eType & (CMDFLAG_SETTING|CMDFLAG_TOPIC))==0;
408
if( hasFunc ){
409
if( strncmp(&zLine[i],"void",4)!=0 ){
410
if( zLine[i]!='*' ) goto page_skip;
411
return;
412
}
413
i += 4;
414
if( !fossil_isspace(zLine[i]) ) goto page_skip;
415
while( fossil_isspace(zLine[i]) ){ i++; }
416
for(j=0; fossil_isident(zLine[i+j]); j++){}
417
if( j==0 ) goto page_skip;
418
}else{
419
j = 0;
420
}
421
for(k=nHelp-1; k>=0 && fossil_isspace(zHelp[k]); k--){}
422
nHelp = k+1;
423
zHelp[nHelp] = 0;
424
for(k=0; k<nHelp && fossil_isspace(zHelp[k]); k++){}
425
if( k<nHelp ){
426
z = string_dup(&zHelp[k], nHelp-k);
427
}else{
428
z = "";
429
}
430
for(k=nFixed; k<nUsed; k++){
431
aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0;
432
aEntry[k].zFunc = hasFunc ? string_dup(&zLine[i], j) : "0";
433
aEntry[k].zHelp = z;
434
z = 0;
435
aEntry[k].iHelp = nFixed;
436
}
437
if( hasFunc ){
438
i+=j;
439
while( fossil_isspace(zLine[i]) ){ i++; }
440
if( zLine[i]!='(' ) goto page_skip;
441
}
442
nFixed = nUsed;
443
nHelp = 0;
444
return;
445
446
page_skip:
447
for(i=nFixed; i<nUsed; i++){
448
fprintf(stderr,"%s:%d: skipping page \"%s\"\n",
449
zFile, nLine, aEntry[i].zPath);
450
}
451
nUsed = nFixed;
452
}
453
454
/*
455
** Compare two entries
456
*/
457
int e_compare(const void *a, const void *b){
458
const Entry *pA = (const Entry*)a;
459
const Entry *pB = (const Entry*)b;
460
return strcmp(pA->zPath, pB->zPath);
461
}
462
463
/*
464
** Build the binary search table.
465
*/
466
void build_table(void){
467
int i;
468
int nWeb = 0;
469
int mxLen = 0;
470
471
qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare);
472
473
printf(
474
"/* Automatically generated code\n"
475
"** DO NOT EDIT!\n"
476
"**\n"
477
"** This file was generated by the mkindex.exe program based on\n"
478
"** comments in other Fossil source files.\n"
479
"*/\n"
480
);
481
482
/* Output declarations for all the action functions */
483
for(i=0; i<nFixed; i++){
484
if( aEntry[i].eType & (CMDFLAG_SETTING|CMDFLAG_TOPIC) ) continue;
485
if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
486
printf("extern void %s(void);\n", aEntry[i].zFunc);
487
if( aEntry[i].zIf ) printf("#endif\n");
488
}
489
490
/* Output strings for all the help text */
491
for(i=0; i<nFixed; i++){
492
char *z = aEntry[i].zHelp;
493
if( z==0 ) continue;
494
if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
495
printf("static const char zHelp%03d[] =\n \"", aEntry[i].iHelp);
496
while( *z ){
497
if( *z=='\n' ){
498
printf("\\n\"\n \"");
499
}else if( *z=='"' ){
500
printf("\\\"");
501
}else{
502
putchar(*z);
503
}
504
z++;
505
}
506
printf("\";\n");
507
if( aEntry[i].zIf ) printf("#endif\n");
508
}
509
510
/* Generate the aCommand[] table */
511
printf("static const CmdOrPage aCommand[] = {\n");
512
for(i=0; i<nFixed; i++){
513
const char *z = aEntry[i].zPath;
514
int n = strlen(z);
515
if( n>mxLen ) mxLen = n;
516
if( aEntry[i].zIf ){
517
printf("%s", aEntry[i].zIf);
518
}else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){
519
nWeb++;
520
}
521
printf(" { \"%.*s\",%*s%s,%*szHelp%03d, %3d, 0x%05x },\n",
522
n, z,
523
25-n, "",
524
aEntry[i].zFunc,
525
(int)(29-strlen(aEntry[i].zFunc)), "",
526
aEntry[i].iHelp,
527
aEntry[i].iHelp,
528
aEntry[i].eType
529
);
530
if( aEntry[i].zIf ) printf("#endif\n");
531
}
532
printf("};\n");
533
printf("#define FOSSIL_FIRST_CMD %d\n", nWeb);
534
printf("#define FOSSIL_MX_CMDNAME %d /* max length of any command name */\n",
535
mxLen);
536
printf("#define FOSSIL_MX_CMDIDX %d /* max index for commands */\n", nFixed);
537
538
/* Generate the aSetting[] table */
539
printf("const Setting aSetting[] = {\n");
540
for(i=0; i<nFixed; i++){
541
const char *z;
542
const char *zVar;
543
const char *zDef;
544
if( (aEntry[i].eType & CMDFLAG_SETTING)==0 ) continue;
545
z = aEntry[i].zPath;
546
zVar = aEntry[i].zVar;
547
zDef = aEntry[i].zDflt;
548
if( zDef==0 ) zDef = "";
549
if( aEntry[i].zIf ){
550
printf("%s", aEntry[i].zIf);
551
}
552
printf(" { \"%s\",%*s", z, (int)(20-strlen(z)), "");
553
if( zVar ){
554
printf(" \"%s\",%*s", zVar, (int)(15-strlen(zVar)), "");
555
}else{
556
printf(" 0,%*s", 16, "");
557
}
558
printf(" %3d, %d, %d, %d, \"%s\"%*s },\n",
559
aEntry[i].iWidth,
560
(aEntry[i].eType & CMDFLAG_VERSIONABLE)!=0,
561
(aEntry[i].eType & CMDFLAG_BLOCKTEXT)!=0,
562
(aEntry[i].eType & CMDFLAG_SENSITIVE)!=0,
563
zDef, (int)(10-strlen(zDef)), ""
564
);
565
if( aEntry[i].zIf ){
566
printf("#endif\n");
567
}
568
}
569
printf("{0,0,0,0,0,0,0}};\n");
570
571
}
572
573
/*
574
** Process a single file of input
575
*/
576
void process_file(void){
577
FILE *in = fopen(zFile, "r");
578
char zLine[2000];
579
if( in==0 ){
580
fprintf(stderr,"%s: cannot open\n", zFile);
581
return;
582
}
583
nLine = 0;
584
while( fgets(zLine, sizeof(zLine), in) ){
585
nLine++;
586
scan_for_if(zLine);
587
scan_for_label("WEBPAGE:",zLine,CMDFLAG_WEBPAGE);
588
scan_for_label("COMMAND:",zLine,CMDFLAG_COMMAND);
589
scan_for_func(zLine);
590
scan_for_label("SETTING:",zLine,CMDFLAG_SETTING);
591
scan_for_default(zLine);
592
scan_for_label("TOPIC:",zLine,CMDFLAG_TOPIC);
593
}
594
fclose(in);
595
nUsed = nFixed;
596
}
597
598
int main(int argc, char **argv){
599
int i;
600
memset(aEntry, 0, sizeof(Entry) * N_ENTRY);
601
for(i=1; i<argc; i++){
602
zFile = argv[i];
603
process_file();
604
}
605
build_table();
606
return nErr;
607
}
608

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button