2
* Entry point, arguments and options.
3
* Copyright (c) Karl Dahlke, 2006
4
* This file is part of the edbrowse project, released under GPL.
12
/* Define the globals that are declared in eb.h. */
13
/* See eb.h for descriptive comments. */
15
const char *version = "3.3.1";
16
char *userAgents[10], *currentAgent, *currentReferrer;
17
const char eol[] = "\r\n";
18
char EMPTYSTRING[] = "";
20
int webTimeout = 20, mailTimeout = 0;
21
bool ismc, browseLocal, zapMail, unformatMail, passMail, errorExit;
22
bool isInteractive, intFlag, inInput;
23
int fileSize, maxFileSize = 50000000;
24
int localAccount, maxAccount;
25
struct MACCOUNT accounts[MAXACCOUNT];
27
struct MIMETYPE mimetypes[MAXMIME];
29
static struct DBTABLE dbtables[MAXDBT];
30
char *dbarea, *dblogin, *dbpw; /* to log into the database */
33
bool caseInsensitive, searchStringsAll;
34
bool textAreaDosNewlines = true, undoable;
35
bool allowRedirection = true, allowJS = true, sendReferrer = false;
36
bool verifyCertificates = true, binaryDetect = true;
38
bool showHiddenFiles, helpMessagesOn;
39
uchar dirWrite, endMarks;
41
uchar linePending[MAXTTYLINE];
42
char *changeFileName, *mailDir;
43
char *addressFile, *ipbFile;
44
char *home, *recycleBin, *configFile, *sigFile;
45
char *sslCerts, *cookieFile, *edbrowseTempFile, *spamCan;
47
int textLinesMax, textLinesCount;
49
struct ebSession sessionList[MAXSESSION], *cs;
51
/* Edbrowse functions, defined in the config file */
52
#define MAXEBSCRIPT 500
54
static char *ebScript[MAXEBSCRIPT + 1];
55
static char *ebScriptName[MAXEBSCRIPT + 1];
57
static const char *javaDis[MAXNOJS];
58
static int javaDisCount;
66
static struct FILTERDESC filters[MAXFILTER];
68
static int subjstart = 0;
78
now /= (60 * 60 * 24); /* convert to days */
80
now -= 7; /* leap years */
87
int fh = open(configFile, O_WRONLY | O_TRUNC, 0);
89
debugPrint(0, "warning: cannot update config file");
92
if(write(fh, cfgcopy, cfglen) < cfglen)
93
i_printfExit(MSG_ERBC_NoWrite);
98
junkSubject(const char *s, char key)
104
i_puts(MSG_NoSubject);
108
i_puts(MSG_NoConfig);
112
i_puts(MSG_NoSubjFilter);
124
memcpy(new, cfgcopy, subjstart);
125
sprintf(new + subjstart, "%d`%s > x\n", exp, s);
126
memcpy(new + subjstart + l, cfgcopy + subjstart, cfglen - subjstart);
131
if(n_filters < MAXFILTER - 1) {
132
filters[n_filters].type = 4;
133
filters[n_filters].match = cloneString(s);
134
filters[n_filters].redirect = "x";
140
/* This routine succeeds, or aborts via i_printfExit */
144
char *buf, *s, *t, *v, *q;
145
char *cfglp, *cfgnlp;
149
bool startline = true;
150
bool cfgmodify = false;
151
uchar mailblock = 0, mimeblock = 0, tabblock = 0;
153
int sn = 0; /* script number */
157
struct MACCOUNT *act;
160
static const char *const keywords[] = {
161
"inserver", "outserver", "login", "password", "from", "reply",
163
"type", "desc", "suffix", "protocol", "program",
164
"tname", "tshort", "cols", "keycol",
165
"adbook", "ipblack", "maildir", "agent",
166
"jar", "nojs", "spamcan",
167
"webtimer", "mailtimer", "certfile", "database", "proxy",
171
if(!fileTypeByName(configFile, false))
172
return; /* config file not present */
173
if(!fileIntoMemory(configFile, &buf, &buflen))
175
/* An extra newline won't hurt. */
176
if(buflen && buf[buflen - 1] != '\n')
177
buf[buflen++] = '\n';
179
cfgcopy = allocMem(buflen + 1);
180
memcpy(cfgcopy, buf, (cfglen = buflen));
182
/* Undos, uncomment, watch for nulls */
183
/* Encode mail{ as hex 81 m, and other encodings. */
185
for(s = t = v = buf; s < buf + buflen; ++s) {
188
i_printfExit(MSG_ERBC_Nulls, ln);
189
if(c == '\r' && s[1] == '\n')
196
if(c == '#' && startline) {
203
if(stringEqual(last, "}")) {
207
if(stringEqual(last, "}else{")) {
211
if(stringEqual(last, "mail{")) {
216
if(stringEqual(last, "mime{")) {
221
if(stringEqual(last, "table{")) {
226
if(stringEqual(last, "fromfilter{")) {
231
if(stringEqual(last, "tofilter{")) {
236
if(stringEqual(last, "subjfilter{")) {
241
subjstart = s + 1 - buf;
243
if(stringEqual(last, "if(*){")) {
248
if(stringEqual(last, "if(?){")) {
253
if(stringEqual(last, "while(*){")) {
258
if(stringEqual(last, "while(?){")) {
263
if(stringEqual(last, "until(*){")) {
268
if(stringEqual(last, "until(?){")) {
273
if(!strncmp(last, "loop(", 5) && isdigitByte(last[5])) {
275
while(isdigitByte(*q))
277
if(stringEqual(q, "){")) {
285
if(!strncmp(last, "function", 8) &&
286
(last[8] == '+' || last[8] == ':')) {
288
if(*q == 0 || *q == '{' || *q == '(')
289
i_printfExit(MSG_ERBC_NoFnName, ln);
291
i_printfExit(MSG_ERBC_FnDigit, ln);
292
while(isalnumByte(*q))
294
if(q - last - 9 > 10)
295
i_printfExit(MSG_ERBC_FnTooLong, ln);
296
if(*q != '{' || q[1])
297
i_printfExit(MSG_ERBC_SyntaxErr, ln);
309
if(c == ' ' || c == '\t') {
313
if(lidx < sizeof (last) - 1)
320
*t = 0; /* now it's a string */
322
/* Go line by line */
327
for(s = buf, cfglp = cfgcopy; *s; s = t + 1, cfglp = cfgnlp, ++ln) {
328
cfgnlp = strchr(cfglp, '\n') + 1;
331
continue; /* empty line */
332
if(t == s + 1 && *s == '#')
333
continue; /* comment */
334
*t = 0; /* I'll put it back later */
336
/* Gather the filters in a mail filter block */
337
if(mailblock > 1 && !strchr("\x81\x82\x83", *s)) {
340
i_printfExit(MSG_ERBC_NoCondFile, ln);
341
while(v > s && (v[-1] == ' ' || v[-1] == '\t'))
344
i_printfExit(MSG_ERBC_NoMatchStr, ln);
351
while(*v == ' ' || *v == '\t')
354
i_printfExit(MSG_ERBC_MatchNowh, ln, s);
355
if(n_filters == MAXFILTER - 1)
356
i_printfExit(MSG_ERBC_Filters, ln);
357
filters[n_filters].redirect = v;
359
long exp = strtol(s, &v, 10);
360
if(exp > 0 && *v == '`' && v[1]) {
362
filters[n_filters].expire = exp;
365
memcpy(cfglp, cfgnlp, cfgcopy + cfglen - cfgnlp);
366
cfglen -= (cfgnlp - cfglp);
369
} /* filter rule out of date */
372
filters[n_filters].match = s;
373
filters[n_filters].type = mailblock;
382
while(v > s && (v[-1] == ' ' || v[-1] == '\t'))
387
for(q = s; q < v; ++q)
388
if(!isalphaByte(*q)) {
393
n = stringInList(keywords, s);
396
i_printfExit(MSG_ERBC_BadKeyword, s, ln);
397
*v = c; /* put it back */
401
if(n < 8 && mailblock != 1)
402
i_printfExit(MSG_ERBC_MailAttrOut, ln, s);
404
if(n >= 8 && n < 13 && mimeblock != 1)
405
i_printfExit(MSG_ERBC_MimeAttrOut, ln, s);
407
if(n >= 13 && n < 17 && tabblock != 1)
408
i_printfExit(MSG_ERBC_TableAttrOut, ln, s);
410
if(n >= 8 && mailblock)
411
i_printfExit(MSG_ERBC_MailAttrIn, ln, s);
413
if((n < 8 || n >= 13) && mimeblock)
414
i_printfExit(MSG_ERBC_MimeAttrIn, ln, s);
416
if((n < 13 || n >= 17) && tabblock)
417
i_printfExit(MSG_ERBC_TableAttrIn, ln, s);
419
/* act upon the keywords */
426
while(*v == ' ' || *v == '\t')
429
i_printfExit(MSG_ERBC_NoAttr, ln, s);
457
act->inport = atoi(v);
461
act->outport = atoi(v);
466
mt->stream = true, ++v;
496
if(td->ncols == MAXTCOLS)
497
i_printfExit(MSG_ERBC_ManyCols, ln, MAXTCOLS);
498
td->cols[td->ncols++] = v;
509
i_printfExit(MSG_ERBC_KeyNotNb, ln);
510
td->key1 = strtol(v, &v, 10);
511
if(*v == ',' && isdigitByte(v[1]))
512
td->key2 = strtol(v + 1, &v, 10);
513
if(td->key1 > td->ncols || td->key2 > td->ncols)
514
i_printfExit(MSG_ERBC_KeyOutRange, ln, td->ncols);
519
ftype = fileTypeByName(v, false);
520
if(ftype && ftype != 'f')
521
i_printfExit(MSG_ERBC_AbNotFile, v);
526
ftype = fileTypeByName(v, false);
527
if(ftype && ftype != 'f')
528
i_printfExit(MSG_ERBC_IPNotFile, v);
533
if(fileTypeByName(v, false) != 'd')
534
i_printfExit(MSG_ERBC_NotDir, v);
538
for(j = 0; j < 10; ++j)
542
i_printfExit(MSG_ERBC_ManyAgents, ln);
548
ftype = fileTypeByName(v, false);
549
if(ftype && ftype != 'f')
550
i_printfExit(MSG_ERBC_JarNotFile, v);
551
j = open(v, O_WRONLY | O_APPEND | O_CREAT, 0600);
553
i_printfExit(MSG_ERBC_JarNoWrite, v);
558
if(javaDisCount == MAXNOJS)
559
i_printfExit(MSG_ERBC_NoJS, MAXNOJS);
564
i_printfExit(MSG_ERBC_DomainDot, ln, v);
565
javaDis[javaDisCount++] = v;
570
ftype = fileTypeByName(v, false);
571
if(ftype && ftype != 'f')
572
i_printfExit(MSG_ERBC_TrashNotFile, v);
576
webTimeout = atoi(v);
580
mailTimeout = atoi(v);
585
ftype = fileTypeByName(v, false);
586
if(ftype && ftype != 'f')
587
i_printfExit(MSG_ERBC_SSLNoFile, v);
588
j = open(v, O_RDONLY);
590
i_printfExit(MSG_ERBC_SSLNoRead, v);
614
proxy_port = atoi(v);
619
i_printfExit(MSG_ERBC_KeywordNYI, ln, s);
624
if(stringEqual(s, "default") && mailblock == 1) {
625
if(localAccount == maxAccount + 1)
628
i_printfExit(MSG_ERBC_SevDefaults);
629
localAccount = maxAccount + 1;
633
if(*s == '\x82' && s[1] == 0) {
638
i_printfExit(MSG_ERBC_NoInserver, ln);
640
i_printfExit(MSG_ERBC_NoOutserver, ln);
642
i_printfExit(MSG_ERBC_NoLogin, ln);
644
i_printfExit(MSG_ERBC_NPasswd, ln);
646
i_printfExit(MSG_ERBC_NoFrom, ln);
648
i_printfExit(MSG_ERBC_NoReply, ln);
665
i_printfExit(MSG_ERBC_NoType, ln);
667
i_printfExit(MSG_ERBC_NDesc, ln);
668
if(!mt->suffix && !mt->prot)
669
i_printfExit(MSG_ERBC_NoSuffix, ln);
671
i_printfExit(MSG_ERBC_NoProgram, ln);
679
i_printfExit(MSG_ERBC_NoTblName, ln);
681
i_printfExit(MSG_ERBC_NoShortName, ln);
683
i_printfExit(MSG_ERBC_NColumns, ln);
688
i_printfExit(MSG_ERBC_UnexpBrace, ln);
691
/* This ends the function */
692
*s = 0; /* null terminate the script */
697
if(*s == '\x83' && s[1] == 0) {
698
/* Does else make sense here? */
699
c = toupper(stack[nest]);
701
i_printfExit(MSG_ERBC_UnexElse, ln);
707
i_printfExit(MSG_ERBC_GarblText, ln);
711
/* Starting something */
713
if((nest || mailblock || mimeblock) && strchr("fmerts", c)) {
714
const char *curblock = "another function";
716
curblock = "a mail descriptor";
718
curblock = "a filter block";
720
curblock = "a mime descriptor";
721
i_printfExit(MSG_ERBC_FnNotStart, ln, curblock);
724
if(!strchr("fmertsb", c) && !nest)
725
i_printfExit(MSG_ERBC_StatNotInFn, ln);
729
if(maxAccount == MAXACCOUNT)
730
i_printfExit(MSG_ERBC_ManyAcc, MAXACCOUNT);
731
act = accounts + maxAccount;
737
if(maxMime == MAXMIME)
738
i_printfExit(MSG_ERBC_ManyTypes, MAXMIME);
739
mt = mimetypes + maxMime;
745
if(maxTables == MAXDBT)
746
i_printfExit(MSG_ERBC_ManyTables, MAXDBT);
747
td = dbtables + maxTables;
768
if(sn == MAXEBSCRIPT)
769
i_printfExit(MSG_ERBC_ManyFn, sn);
770
ebScriptName[sn] = s + 2;
776
if(++nest >= sizeof (stack))
777
i_printfExit(MSG_ERBC_TooDeeply, ln);
782
} /* loop over lines */
785
i_printfExit(MSG_ERBC_FnNotClosed, ebScriptName[sn]);
787
if(mailblock | mimeblock)
788
i_printfExit(MSG_ERBC_MNotClosed);
792
} /* readConfigFile */
795
/*********************************************************************
796
Redirect the incoming mail into a file, based on the subject or the sender.
797
Along with the filters in .ebrc, this routine dips into your addressbook,
798
to see if the sender (by email) is one of your established aliases.
799
If it is, we save it in a file of the same name.
800
This is saved formatted, unless you put a minus sign
801
at the start of the alias in your address book.
802
This is the same convention as the from filters in .ebrc.
803
If you don't want an alias to act as a redirect filter,
804
put a ! at the beginning of the alias name.
805
*********************************************************************/
808
mailRedirect(const char *to, const char *from,
809
const char *reply, const char *subj)
811
int rlen = strlen(reply);
812
int slen = strlen(subj);
813
int tlen = strlen(to);
814
struct FILTERDESC *f;
817
for(f = filters; f->match; ++f) {
818
const char *m = f->match;
819
int mlen = strlen(m);
825
if(stringEqualCI(m, from))
827
if(stringEqualCI(m, reply))
829
if(*m == '@' && mlen < rlen &&
830
stringEqualCI(m, reply + rlen - mlen))
835
if(stringEqualCI(m, to))
837
if(*m == '@' && mlen < tlen && stringEqualCI(m, to + tlen - mlen))
842
if(stringEqualCI(m, subj))
844
/* a prefix match is ok */
845
if(slen < 16 || mlen < 16)
846
break; /* too short */
859
for(++j; c == subj[j]; ++j) ;
860
for(++k; d == m[k]; ++k) ;
862
/* must match at least 2/3 of either string */
865
if(j >= 2 * mlen / 3 || j >= 2 * slen / 3) {
872
r = reverseAlias(reply);
877
/*********************************************************************
878
Are we ok to parse and execute javascript?
879
*********************************************************************/
882
javaOK(const char *url)
885
const char *h, *d, *q, *path;
894
path = getDataURL(url);
895
for(j = 0; j < javaDisCount; ++j) {
903
if(!memEqualCI(d, h + hl - dl, dl))
911
if(strncmp(q, path, strlen(q)))
914
} /* domain/path was specified */
917
if(h[hl - dl - 1] == '.')
923
/* Catch interrupt and react appropriately. */
929
i_puts(MSG_EnterInterrupt);
930
/* If we were reading from a file, or socket, this signal should
931
* cause the read to fail. Check for intFlag, so we know it was
932
* interrupted, and not an io failure.
933
* Then clean up appropriately. */
934
signal(SIGINT, catchSig);
951
/* I'm not going to expand wild card arguments here.
952
* I don't need to on Unix, and on Windows there is a
953
* setargv.obj, or something like that, that performs the expansion.
954
* I'll assume you have folded that object into libc.lib.
955
* So now you can edit *.c, on any operating system,
956
* and it will do the right thing, with no work on my part. */
959
main(int argc, char **argv)
962
bool rc, doConfig = true;
968
/* Let's everybody use my malloc and free routines */
969
pcre_malloc = allocMem;
972
/* Establish the home directory, and standard edbrowse files thereunder. */
973
home = getenv("HOME");
974
/* Empty is the same as missing. */
977
/* I require this, though I'm not sure what this means for non-Unix OS's */
979
i_printfExit(MSG_NotHome);
980
if(fileTypeByName(home, false) != 'd')
981
i_printfExit(MSG_NotDir, home);
983
/* See sample.ebrc in this directory for a sample config file. */
984
configFile = allocMem(strlen(home) + 7);
985
sprintf(configFile, "%s/.ebrc", home);
987
recycleBin = allocMem(strlen(home) + 10);
988
sprintf(recycleBin, "%s/.recycle", home);
989
edbrowseTempFile = allocMem(strlen(recycleBin) + 8 + 6);
990
/* The extra 6 is for the suffix */
991
sprintf(edbrowseTempFile, "%s/eb_tmp", recycleBin);
992
if(fileTypeByName(recycleBin, false) != 'd') {
993
if(mkdir(recycleBin, 0700)) {
999
sigFile = allocMem(strlen(home) + 12);
1000
sprintf(sigFile, "%s/.signature", home);
1003
static char agent0[32] = "edbrowse/";
1004
strcat(agent0, version);
1005
userAgents[0] = currentAgent = agent0;
1011
if(argc && stringEqual(argv[0], "-c")) {
1019
if(maxAccount && !localAccount)
1022
account = localAccount;
1024
for(; argc && argv[0][0] == '-'; ++argv, --argc) {
1027
if(stringEqual(s, "v")) {
1031
if(stringEqual(s, "d")) {
1035
if(*s == 'd' && isdigitByte(s[1]) && !s[2]) {
1036
debugLevel = s[1] - '0';
1039
if(stringEqual(s, "e")) {
1044
++s, unformatMail = true;
1046
++s, passMail = true;
1047
if(*s == 'm' && isdigitByte(s[1])) {
1049
i_printfExit(MSG_NoMailAcc);
1050
account = strtol(s + 1, &s, 10);
1051
if(account == 0 || account > maxAccount)
1052
i_printfExit(MSG_BadAccNb, maxAccount);
1054
ismc = true; /* running as a mail client */
1055
allowJS = false; /* no javascript in mail client */
1056
++argv, --argc; /* we're going to break out */
1057
if(argc && stringEqual(argv[0], "-Zap"))
1058
++argv, --argc, zapMail = true;
1062
i_printfExit(MSG_Usage);
1066
debugPrint(4, "tcp failure, could not identify this machine");
1068
debugPrint(4, "host info established for %s, %s",
1069
tcp_thisMachineName, tcp_thisMachineDots);
1078
char **reclist, **atlist;
1080
int nat, nalt, nrec;
1084
i_printfExit(MSG_MinOneRec);
1085
/* I don't know that argv[argc] is 0, or that I can set it to 0,
1086
* so I back everything up by 1. */
1088
for(nat = nalt = 0; nat < argc; ++nat) {
1089
s = argv[argc - 1 - nat];
1090
if(*s != '+' && *s != '-')
1096
atlist = argv + argc - nat - 1;
1098
i_printfExit(MSG_MinOneRecBefAtt);
1101
memcpy(atlist, atlist + 1, sizeof (char *) * nat);
1103
nrec = atlist - argv;
1104
memcpy(reclist, reclist + 1, sizeof (char *) * nrec);
1106
if(sendMail(account, (const char **)reclist, body, 1,
1107
(const char **)atlist, nalt, true))
1115
signal(SIGINT, catchSig);
1116
siginterrupt(SIGINT, 1);
1117
signal(SIGPIPE, SIG_IGN);
1124
if(cx == MAXSESSION)
1125
i_printfExit(MSG_ManyOpen, MAXSESSION);
1126
cxSwitch(cx, false);
1128
runEbFunction("init");
1130
fetchHistory(0, 0); /* reset history */
1131
cw->fileName = cloneString(file);
1134
rc = readFile(file, "");
1136
debugPrint(1, "%d", fileSize);
1140
} else if(changeFileName) {
1141
nzFree(cw->fileName);
1142
cw->fileName = changeFileName;
1145
if(cw->fileName && memEqualCI(cw->fileName, "ftp://", 6)) {
1146
nzFree(cw->fileName);
1149
cw->firstOpMode = cw->changeMode = false;
1150
/* Browse the text if it's a url */
1151
if(rc && !(cw->binMode | cw->dirMode) && cw->dol && isURL(cw->fileName)) {
1153
debugPrint(1, "%d", fileSize);
1158
} /* loop over files */
1159
if(!cx) { /* no files */
1161
cxSwitch(cx, false);
1162
runEbFunction("init");
1170
uchar saveline[MAXTTYLINE];
1171
pst p = inputLine();
1172
copyPstring(saveline, p);
1173
if(perl2c((char *)p))
1174
i_puts(MSG_EnterNull);
1176
edbrowseCommand((char *)p, false);
1177
copyPstring(linePending, saveline);
1178
} /* infinite loop */
1181
/* Find the balancing brace in an edbrowse function */
1183
balance(const char *ip, int direction)
1190
ip = strchr(ip, '\n') + 1;
1192
for(ip -= 2; *ip != '\n'; --ip) ;
1212
/* Run an edbrowse function, as defined in the config file. */
1214
runEbFunction(const char *line)
1216
char *linecopy = cloneString(line);
1217
const char *args[10];
1218
int argl[10]; /* lengths of args */
1219
int argtl; /* total length */
1223
const char *ip; /* think instruction pointer */
1224
const char *endl; /* end of line to be processed */
1227
char stack[MAXNEST];
1228
int loopcnt[MAXNEST];
1230
/* Separate function name and arguments */
1231
spaceCrunch(linecopy, true, false);
1232
if(linecopy[0] == 0) {
1233
setError(MSG_NoFunction);
1236
memset(args, 0, sizeof (args));
1237
memset(argl, 0, sizeof (argl));
1239
t = strchr(linecopy, ' ');
1242
for(s = linecopy; *s; ++s)
1243
if(!isalnumByte(*s)) {
1244
setError(MSG_BadFunctionName);
1247
for(j = 0; ebScript[j]; ++j)
1248
if(stringEqual(linecopy, ebScriptName[j] + 1))
1251
setError(MSG_NoSuchFunction, linecopy);
1255
/* skip past the leading \n */
1256
ip = ebScript[j] + 1;
1257
nofail = (ebScriptName[j][0] == '+');
1261
/* collect arguments */
1263
for(s = t; s; s = t) {
1265
setError(MSG_ManyArgs);
1274
argtl += (argl[j] = strlen(s));
1280
setError(MSG_Interrupted);
1283
endl = strchr(ip, '\n');
1286
ip = balance(ip, 1) + 2;
1292
char control = stack[nest];
1293
char ucontrol = toupper(control);
1294
const char *start = balance(ip, -1);
1295
start = strchr(start, '\n') + 1;
1296
if(ucontrol == 'L') { /* loop */
1300
ip = endl + 1, --nest;
1303
if(ucontrol == 'W' || ucontrol == 'U') {
1305
if(islowerByte(control))
1313
ip = endl + 1, --nest;
1316
/* Apparently it's the close of an if or an else, just fall through */
1321
const char *skip = balance(ip, 1);
1323
char control = ip[1];
1324
char ucontrol = toupper(control);
1325
stack[++nest] = control;
1326
if(ucontrol == 'L') {
1327
loopcnt[nest] = j = atoi(ip + 2);
1331
if(*skip == (char)0x82)
1338
/* if or while, test on ok */
1340
if(isupperByte(control))
1351
/* compute length of line, then build the line */
1353
for(s = ip; s < endl; ++s)
1354
if(*s == '~' && isdigitByte(s[1]))
1355
l += argl[s[1] - '0'];
1356
t = new = allocMem(l + 1);
1357
for(s = ip; s < endl; ++s) {
1358
if(*s == '~' && isdigitByte(s[1])) {
1362
setError(MSG_NoArgument, j);
1370
/* ~0 is all args together */
1371
for(j = 1; j <= 9 && args[j]; ++j) {
1384
debugPrint(3, "< %s", new);
1385
ok = edbrowseCommand(new, true);
1401
} /* runEbFunction */
1403
/* Send the contents of the current buffer to a running program */
1405
bufferToProgram(const char *cmd, const char *suffix, bool trailPercent)
1411
char *u = edbrowseTempFile + strlen(edbrowseTempFile);
1414
/* pipe the buffer into the program */
1415
f = popen(cmd, "w");
1417
setError(MSG_NoSpawn, cmd, errno);
1420
if(!unfoldBuffer(context, false, &buf, &buflen)) {
1422
return false; /* should never happen */
1424
n = fwrite(buf, buflen, 1, f);
1427
sprintf(u, ".%s", suffix);
1428
size1 = currentBufferSize();
1429
size2 = fileSizeByName(edbrowseTempFile);
1430
if(size1 == size2) {
1431
/* assume it's the same data */
1434
f = fopen(edbrowseTempFile, "w");
1436
setError(MSG_TempNoCreate2, edbrowseTempFile, errno);
1441
if(!unfoldBuffer(context, false, &buf, &buflen)) {
1443
return false; /* should never happen */
1445
n = fwrite(buf, buflen, 1, f);
1453
} /* bufferToProgram */
1456
findTableDescriptor(const char *sn)
1459
struct DBTABLE *td = dbtables;
1460
for(i = 0; i < maxTables; ++i, ++td)
1461
if(stringEqual(td->shortname, sn))
1464
} /* findTableDescriptor */
1467
newTableDescriptor(const char *name)
1470
if(maxTables == MAXDBT) {
1471
setError(MSG_ManyTables, MAXDBT);
1474
td = dbtables + maxTables++;
1475
td->name = td->shortname = cloneString(name);
1476
td->ncols = 0; /* it's already 0 */
1478
} /* newTableDescriptor */
1481
findMimeBySuffix(const char *suffix)
1484
int len = strlen(suffix);
1485
struct MIMETYPE *m = mimetypes;
1487
for(i = 0; i < maxMime; ++i, ++m) {
1488
const char *s = m->suffix, *t;
1495
if(t - s == len && memEqualCI(s, suffix, len))
1504
} /* findMimeBySuffix */
1507
findMimeByProtocol(const char *prot)
1510
int len = strlen(prot);
1511
struct MIMETYPE *m = mimetypes;
1513
for(i = 0; i < maxMime; ++i, ++m) {
1514
const char *s = m->prot, *t;
1521
if(t - s == len && memEqualCI(s, prot, len))
1530
} /* findMimeByProtocol */
1532
/* The result is allocated */
1534
pluginCommand(const struct MIMETYPE *m, const char *file, const char *suffix)
1539
bool trailPercent = false;
1541
/* leave rooom for space quote quote null */
1542
len = strlen(m->program) + 4;
1544
len += strlen(file);
1545
} else if(m->program[strlen(m->program) - 1] == '%') {
1546
trailPercent = true;
1547
len += strlen(edbrowseTempFile) + 6;
1552
suflen = strlen(suffix);
1553
for(s = m->program; *s; ++s)
1558
cmd = allocMem(len);
1560
for(s = m->program; *s; ++s) {
1561
if(suffix && *s == '*') {
1571
sprintf(t, " \"%s\"", file);
1572
} else if(trailPercent) {
1573
sprintf(t - 1, " \"%s.%s\"", edbrowseTempFile, suffix);
1576
debugPrint(3, "%s", cmd);
1578
} /* pluginCommand */