Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/fio.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2012 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* Mail -- a mail program
71
*
72
* File I/O.
73
*/
74
75
#include "mailx.h"
76
77
#include <fts.h>
78
79
/*
80
* Return next msg struct for current folder.
81
*/
82
struct msg*
83
newmsg(off_t offset)
84
{
85
register struct msg* mp;
86
unsigned long dot;
87
88
if (state.msg.count >= state.msg.size) {
89
dot = state.msg.dot - state.msg.list;
90
state.msg.size += 256;
91
if (!(state.msg.list = newof(state.msg.list, struct msg, state.msg.size, 0)))
92
note(PANIC, "Insufficient memory for %d messages", state.msg.count);
93
state.msg.dot = state.msg.list + dot;
94
}
95
mp = state.msg.list + state.msg.count++;
96
mp->m_index = 0;
97
mp->m_flag = MUSED|MNEW;
98
mp->m_size = 0;
99
mp->m_lines = 0;
100
mp->m_block = blocknumber(offset);
101
mp->m_offset = blockoffset(offset);
102
mp->m_info = 0;
103
return mp;
104
}
105
106
/*
107
* Set up the input pointers while copying the mail file into /tmp.
108
*/
109
void
110
setptr(register FILE* ibuf, off_t offset)
111
{
112
register struct msg* mp;
113
register int c;
114
register int count;
115
register int prevcount;
116
register char* cp;
117
register char* cp2;
118
struct match* xp;
119
int maybe;
120
int inhead;
121
off_t roff;
122
off_t zoff;
123
char buf[LINESIZE];
124
125
if (roff = offset) {
126
/* Seek into the file to get to the new messages */
127
fseek(ibuf, offset, SEEK_SET);
128
/*
129
* We need to make "offset" a pointer to the end of
130
* the temp file that has the copy of the mail file.
131
* If any messages have been edited, this will be
132
* different from the offset into the mail file.
133
*/
134
fseek(state.msg.op, (off_t)0, SEEK_END);
135
offset = ftell(state.msg.op);
136
}
137
else
138
state.msg.count = 0;
139
state.folder = FFILE;
140
maybe = 1;
141
inhead = 0;
142
mp = 0;
143
count = 0;
144
for (;;) {
145
cp = buf;
146
if (state.var.headfake) {
147
state.var.headfake = 0;
148
strcopy(cp, "From bozo@bigtop Wed Feb 29 00:00:00 2012\r\n");
149
}
150
else if (!fgets(cp, LINESIZE, ibuf))
151
break;
152
prevcount = count;
153
count = strlen(cp);
154
if (count == 0 && (zoff = ftell(ibuf)) > roff) {
155
if ((count = zoff - roff) > LINESIZE)
156
count = LINESIZE;
157
for (cp2 = cp + count; cp < cp2 && *cp == 0; cp++);
158
if (count = cp - buf)
159
note(WARNING, "%d nul%s at offset %lld", count, count == 1 ? "" : "s", (Sflong_t)roff);
160
count = cp2 - cp;
161
}
162
else if (count >= 2 && cp[count - 1] == '\n' && cp[count - 2] == '\r') {
163
cp[count - 2] = '\n';
164
count--;
165
}
166
if (fwrite(cp, 1, count, state.msg.op) != count)
167
note(FATAL|SYSTEM, "Temporary file");
168
cp[count - 1] = 0;
169
if (state.bodymatch && !inhead && mp && !(mp->m_flag & MSCAN) && count >= state.bodymatch->minline && state.bodymatch->beg[cp[0]] && state.bodymatch->mid[cp[state.bodymatch->minline/2]] && state.bodymatch->end[cp[state.bodymatch->minline-1]])
170
for (xp = state.bodymatch->match; xp; xp = xp->next)
171
if (count >= xp->length && cp[0] == xp->beg && cp[xp->length/2] == xp->mid && cp[xp->length-1] == xp->end && !memcmp(cp, xp->string, xp->length))
172
{
173
if (TRACING('x'))
174
note(0, "spam: body match `%s'", xp->string);
175
msgflags(mp, MSCAN|MSPAM, 0);
176
break;
177
}
178
if (maybe && ishead(cp, inhead || prevcount != 1)) {
179
mp = newmsg(offset);
180
inhead = 1;
181
}
182
else if (maybe = !cp[0])
183
inhead = 0;
184
else if (inhead) {
185
for (cp2 = "status";; cp++) {
186
if ((c = *cp2++) == 0) {
187
while (isspace(*cp++))
188
;
189
if (cp[-1] != ':')
190
break;
191
for (;;) {
192
switch (*cp++) {
193
case 0:
194
break;
195
case 'O':
196
msgflags(mp, 0, MNEW);
197
goto scanned;
198
case 'R':
199
msgflags(mp, MREAD, 0);
200
goto scanned;
201
case 'X':
202
msgflags(mp, MSPAM, 0);
203
goto scanned;
204
default:
205
continue;
206
scanned:
207
if (!state.edit)
208
msgflags(mp, MSCAN, 0);
209
continue;
210
}
211
break;
212
}
213
inhead = 0;
214
break;
215
}
216
if (c != lower(*cp))
217
break;
218
}
219
}
220
roff += count;
221
offset += count;
222
if (mp) {
223
mp->m_size += count;
224
mp->m_lines++;
225
}
226
}
227
}
228
229
/*
230
* fts sort order to find max entry
231
*/
232
static int
233
dirmax(FTSENT* const* a, FTSENT* const* b)
234
{
235
return strtol((*b)->fts_name, NiL, 10) - strtol((*a)->fts_name, NiL, 10);
236
}
237
238
/*
239
* mh context file names
240
*/
241
static const char* mh_context[] = {
242
".current",
243
".mh_sequences",
244
".exmhcontext",
245
"../.exmhcontext",
246
"../../.exmhcontext",
247
};
248
249
/*
250
* Return the mh message context for folder name.
251
*/
252
void
253
mhgetcontext(register struct mhcontext* xp, const char* name, int next)
254
{
255
register char* s;
256
register int n;
257
register int i;
258
register FTS* fts;
259
register FTSENT* ent;
260
char* e;
261
FILE* fp;
262
struct stat ds;
263
struct stat fs;
264
char buf[LINESIZE];
265
266
xp->type = 0;
267
xp->dot = xp->next = 0;
268
i = strlen(name);
269
for (n = 0; n < elementsof(mh_context); n++) {
270
sfprintf(state.path.temp, "%s/%s", name, mh_context[n]);
271
if (fp = fileopen(struse(state.path.temp), "r")) {
272
while (s = fgets(buf, sizeof(buf), fp)) {
273
if (!strncasecmp(s, "mhcurmsg=", 9)) {
274
xp->dot = strtol(s + 9, &e, 10);
275
for (s = e; isspace(*s); s++);
276
if (!strncasecmp(s, "mhlastmsg=", 10))
277
xp->next = strtol(s + 10, NiL, 10) + 1;
278
if (next && !fstat(fileno(fp), &fs) && !stat(name, &ds) && fs.st_mtime >= ds.st_mtime)
279
next = 0;
280
xp->type = n + 1;
281
break;
282
}
283
else if (!strncasecmp(s, "cur:", 4)) {
284
xp->dot = strtol(s + 4, NiL, 10);
285
xp->type = n + 1;
286
break;
287
}
288
else if (!strncasecmp(s, "atr-cur-", 8) && !strncmp(s += 8, name, i) && *(s += i) == ':') {
289
xp->dot = strtol(s + 1, NiL, 10);
290
xp->type = n + 1;
291
break;
292
}
293
}
294
fileclose(fp);
295
}
296
}
297
if (next && (fts = fts_open((char**)name, FTS_ONEPATH|FTS_NOCHDIR|FTS_NOPOSTORDER, dirmax))) {
298
while (ent = fts_read(fts)) {
299
xp->next = strtol(ent->fts_name, &e, 10) + 1;
300
if (!*e)
301
break;
302
if (ent->fts_level > 0)
303
fts_set(NiL, ent, FTS_SKIP);
304
}
305
fts_close(fts);
306
}
307
if (xp->next <= 0)
308
xp->next = 1;
309
if (next && !xp->type)
310
xp->type = 1;
311
xp->old.dot = xp->dot;
312
xp->old.next = xp->next;
313
}
314
315
/*
316
* Update the mh message context for folder name.
317
*/
318
void
319
mhputcontext(register struct mhcontext* xp, const char* name)
320
{
321
FILE* fp;
322
323
if (xp->type == 1) {
324
if (xp->dot != xp->old.dot || xp->next != xp->old.next) {
325
sfprintf(state.path.temp, "%s/%s", name, mh_context[xp->type - 1]);
326
if (fp = fileopen(struse(state.path.temp), "Ew")) {
327
fprintf(fp, "MhCurmsg=%ld MhLastmsg=%ld\n", xp->dot, xp->next - 1);
328
fileclose(fp);
329
}
330
}
331
}
332
else if (xp->type) {
333
if (xp->dot != xp->old.dot) {
334
note(0, "%s: don't know how to update context yet", name);
335
}
336
}
337
}
338
339
/*
340
* fts sort order
341
*/
342
static int
343
dircmp(FTSENT* const* a, FTSENT* const* b)
344
{
345
return strtol((*a)->fts_name, NiL, 10) - strtol((*b)->fts_name, NiL, 10);
346
}
347
348
/*
349
* Set up pointers to folder directory.
350
*/
351
int
352
mh_setptr(char* name, int isedit)
353
{
354
register struct msg* mp;
355
register FTS* fts;
356
register FTSENT* ent;
357
register char* s;
358
register int n;
359
unsigned long index;
360
unsigned long next;
361
int status;
362
char* e;
363
FILE* fp;
364
char buf[LINESIZE];
365
366
if (!(fts = fts_open((char**)name, FTS_ONEPATH|FTS_NOCHDIR|FTS_NOPOSTORDER, dircmp))) {
367
note(SYSTEM, "%s", name);
368
return -1;
369
}
370
holdsigs();
371
settmp(name, 1);
372
state.edit = isedit;
373
state.mailsize = 0;
374
state.msg.count = 0;
375
state.msg.active = 0;
376
next = 0;
377
mp = 0;
378
while (ent = fts_read(fts)) {
379
index = strtol(ent->fts_name, &e, 10);
380
if (!*e) {
381
next = index;
382
if ((ent->fts_info & FTS_F) && (fp = fileopen(ent->fts_accpath, "Er"))) {
383
for (;;) {
384
mp = newmsg(0);
385
if (state.msg.count >= index)
386
break;
387
mp->m_flag = MUSED|MNONE;
388
}
389
mp->m_size = ent->fts_statp->st_size;
390
status = 0;
391
while (s = fgets(buf, sizeof(buf), fp)) {
392
mp->m_lines++;
393
if (!status) {
394
n = strlen(s);
395
if (n <= 1)
396
status = 1;
397
else if (!strncasecmp(s, "status:", 7)) {
398
status = 1;
399
for (s += 7;;) {
400
switch (*s++) {
401
case 0:
402
break;
403
case 'O':
404
msgflags(mp, 0, MNEW);
405
continue;
406
case 'R':
407
msgflags(mp, MREAD, 0);
408
continue;
409
case 'X':
410
msgflags(mp, MSPAM, 0);
411
continue;
412
default:
413
continue;
414
}
415
break;
416
}
417
}
418
}
419
}
420
fileclose(fp);
421
}
422
else
423
fts_set(NiL, ent, FTS_SKIP);
424
}
425
else if (ent->fts_level > 0)
426
fts_set(NiL, ent, FTS_SKIP);
427
}
428
fts_close(fts);
429
mhgetcontext(&state.msg.mh, name, 0);
430
state.folder = FMH;
431
while (state.msg.mh.dot < state.msg.count && ((state.msg.list + state.msg.mh.dot - 1)->m_flag & MNONE))
432
state.msg.mh.dot++;
433
if (state.msg.mh.dot > 0 && state.msg.mh.dot <= state.msg.count)
434
state.msg.context = state.msg.list + state.msg.mh.dot - 1;
435
state.msg.mh.next = next + 1;
436
if (!state.msg.list) {
437
newmsg(0);
438
state.msg.count = 0;
439
}
440
state.msg.inbox = state.var.inbox && (s = expand(state.var.inbox, 1)) && streq(s, state.path.mail);
441
relsesigs();
442
state.sawcom = 0;
443
return 0;
444
}
445
446
/*
447
* Drop the passed line onto the passed output buffer.
448
* If a write error occurs, return -1, else the count of
449
* characters written, including the newline.
450
*/
451
int
452
putline(FILE* obuf, char* buf)
453
{
454
register int c;
455
register int x;
456
457
x = 1;
458
if (strneq(buf, "From ", 5)) {
459
if (putc('>', obuf) == EOF)
460
return -1;
461
x++;
462
}
463
c = strlen(buf);
464
if (fwrite(buf, 1, c, obuf) != c || putc('\n', obuf) == EOF)
465
return -1;
466
return c + x;
467
}
468
469
/*
470
* Read up a line from the specified input into the line
471
* buffer. Return the number of characters read. Do not
472
* include the newline at the end.
473
*/
474
int
475
readline(FILE* ibuf, char* buf, int size)
476
{
477
register int n;
478
479
clearerr(ibuf);
480
if (!fgets(buf, size, ibuf))
481
return -1;
482
n = strlen(buf);
483
if (n > 0 && buf[n - 1] == '\n') {
484
buf[--n] = 0;
485
if (n > 0 && buf[n - 1] == '\r')
486
buf[--n] = 0;
487
}
488
return n;
489
}
490
491
/*
492
* Return a file buffer all ready to read up the
493
* passed message pointer.
494
*/
495
FILE*
496
setinput(register struct msg* mp)
497
{
498
FILE* fp;
499
off_t offset;
500
501
if (state.folder == FIMAP) {
502
fp = imap_setinput(mp);
503
offset = 0;
504
}
505
else if (state.folder == FFILE || (mp->m_flag & MODIFY)) {
506
if (state.msg.op)
507
fflush(state.msg.op);
508
fp = state.msg.ip;
509
offset = blockposition(mp->m_block, mp->m_offset);
510
}
511
else {
512
if (state.msg.active != mp) {
513
state.msg.active = mp;
514
if (state.msg.ap)
515
fileclose(state.msg.ap);
516
sfprintf(state.path.temp, "%s/%d", state.path.mail, mp - state.msg.list + 1);
517
if (!(state.msg.ap = fileopen(struse(state.path.temp), "EIr")) && !(state.msg.ap = fileopen("/dev/null", "EIr")))
518
note(PANIC|SYSTEM, "Empty file open");
519
}
520
fp = state.msg.ap;
521
offset = 0;
522
}
523
if (fseek(fp, offset, SEEK_SET) < 0)
524
note(PANIC|SYSTEM, "Temporary file seek");
525
if (!(mp->m_flag & MSCAN)) {
526
msgflags(mp, MSCAN, 0);
527
if (state.var.spam && !(mp->m_flag & MSPAM)) {
528
if (spammed(mp))
529
msgflags(mp, MSPAM, 0);
530
if (fseek(fp, offset, SEEK_SET) < 0)
531
note(PANIC|SYSTEM, "Temporary file seek");
532
}
533
}
534
return fp;
535
}
536
537
/*
538
* Delete a file, but only if the file is a plain file.
539
*/
540
int
541
rm(char* name)
542
{
543
if (!isreg(name)) {
544
errno = EFTYPE;
545
return -1;
546
}
547
return remove(name);
548
}
549
550
/*
551
* Determine the size of the file possessed by
552
* the passed buffer.
553
*/
554
off_t
555
filesize(FILE* fp)
556
{
557
struct stat st;
558
559
if (fstat(fileno(fp), &st) < 0)
560
return 0;
561
return st.st_size;
562
}
563
564
#if !_lib_ftruncate
565
int
566
ftruncate(int fd, off_t off)
567
{
568
return 0;
569
}
570
#endif
571
572
/*
573
* Truncate a file to the last character written. This is
574
* useful just before closing an old file that was opened
575
* for read/write.
576
*/
577
int
578
filetrunc(FILE* fp)
579
{
580
return (fflush(fp) || ftruncate(fileno(fp), ftell(fp))) ? -1 : 0;
581
}
582
583
/*
584
* Evaluate the string given as a new mailbox name.
585
* Supported meta characters:
586
* % for my system mail box
587
* %user for user's system mail box
588
* # for previous file
589
* & invoker's mbox file
590
* +file file in folder directory
591
* @name IMAP folder
592
* any shell meta character
593
* Return the file name as a dynamic string.
594
*/
595
char*
596
expand(register char* name, int verify)
597
{
598
register int n;
599
FILE* fp;
600
char buf[LINESIZE];
601
char cmd[LINESIZE];
602
603
/*
604
* The order of evaluation is "%" and "#" expand into constants.
605
* "&" can expand into "+". "+" can expand into shell meta characters.
606
* Shell meta characters expand into constants.
607
* This way, we make no recursive expansion.
608
*/
609
again:
610
switch (name[0]) {
611
case '%':
612
if (!*++name)
613
name = state.var.user;
614
else if (!isalnum(*name))
615
goto again;
616
return mailbox(name, state.var.mail);
617
case '#':
618
if (name[1])
619
break;
620
if (!state.path.prev[0]) {
621
note(0, "No previous file");
622
return 0;
623
}
624
return savestr(state.path.prev);
625
case '&':
626
if (!name[1])
627
name = state.var.mbox;
628
break;
629
}
630
if (name[0] == '+' && getfolder(cmd, sizeof(cmd)) >= 0) {
631
sfsprintf(buf, sizeof(buf), "%s/%s", cmd, name + 1);
632
name = buf;
633
}
634
if (name[0] == '~' && (name[1] == '/' || name[1] == 0)) {
635
if (name == buf) {
636
strncopy(cmd, name, sizeof(cmd));
637
name = cmd;
638
}
639
sfsprintf(buf, sizeof(buf), "%s%s", state.var.home, name + 1);
640
name = buf;
641
}
642
if (!anyof(name, "~{[*?$`'\";<>"))
643
return savestr(name);
644
sfsprintf(cmd, sizeof(cmd), "echo %s", name);
645
name = cmd + 5;
646
if (!(fp = pipeopen(cmd, "r")))
647
return 0;
648
n = fread(buf, 1, sizeof(buf), fp);
649
note(DEBUG, "expand: n=%d \"%-*.s\"", n, n, buf);
650
if (fileclose(fp)) {
651
note(0, "\"%s\": expansion failed", name);
652
return 0;
653
}
654
if (n < 0) {
655
note(SYSTEM, "%s: read error", name);
656
return 0;
657
}
658
if (n >= sizeof(buf)) {
659
note(0, "\"%s\": expansion buffer overflow", name);
660
n = sizeof(buf) - 1;
661
}
662
while (n > 0 && isspace(buf[n - 1]))
663
n--;
664
buf[n] = 0;
665
if (verify) {
666
if (!n) {
667
note(0, "\"%s\": no match", name);
668
return 0;
669
}
670
if (strchr(buf, ' ') && access(buf, F_OK)) {
671
note(0, "\"%s\": ambiguous (%s)", name, buf);
672
return 0;
673
}
674
}
675
return savestr(buf);
676
}
677
678
/*
679
* Determine the current folder directory name.
680
*/
681
int
682
getfolder(char* name, size_t size)
683
{
684
char* folder;
685
686
if (!(folder = state.var.folder))
687
return -1;
688
if (*folder == '/')
689
strncopy(name, folder, size);
690
else
691
sfsprintf(name, size, "%s/%s", state.var.home, folder);
692
return 0;
693
}
694
695
#if MORE_DISCIPLINE
696
697
/*
698
* For mail purposes it's safe to assume line-at-a-time input.
699
*/
700
static ssize_t
701
morein(Sfio_t* fp, void* buf, size_t n, Sfdisc_t* dp)
702
{
703
state.more.match = 0;
704
state.more.row = 2;
705
state.more.col = 1;
706
return sfrd(fp, buf, n, dp);
707
}
708
709
/*
710
* Simple (but fast) more style pager.
711
*/
712
static ssize_t
713
moreout(Sfio_t* fp, const void* buf, size_t n, Sfdisc_t* dp)
714
{
715
register char* b;
716
register char* s;
717
register char* e;
718
register ssize_t w;
719
register size_t m;
720
register int r;
721
722
if (!state.more.row)
723
return n;
724
if (!state.more.col)
725
return sfwr(fp, buf, n, dp);
726
w = 0;
727
b = (char*)buf;
728
s = b;
729
e = s + n;
730
if (state.more.match) {
731
match:
732
for (r = state.more.pattern[0];; s++) {
733
if (s >= e)
734
return n;
735
if (*s == '\n')
736
b = s + 1;
737
else if (*s == r && (e - s) >= state.more.match && !strncmp(s, state.more.pattern, state.more.match))
738
break;
739
}
740
s = b;
741
w += b - (char*)buf;
742
state.more.match = 0;
743
}
744
while (s < e) {
745
switch (*s++) {
746
case '\t':
747
state.more.col = ((state.more.col + 8) & ~7) - 1;
748
/*FALLTHROUGH*/
749
default:
750
if (++state.more.col <= state.screenwidth)
751
continue;
752
/*FALLTHROUGH*/
753
case '\n':
754
state.more.col = 1;
755
if (++state.more.row < state.screenheight)
756
continue;
757
break;
758
case '\b':
759
if (state.more.col > 1)
760
state.more.col--;
761
continue;
762
case '\r':
763
state.more.col = 1;
764
continue;
765
}
766
w += sfwr(fp, b, s - b, dp);
767
b = s;
768
r = ttyquery(0, 1, *state.var.more ? state.var.more : "[More]");
769
if (r == '/' || r == 'n') {
770
if (r == '/') {
771
sfwr(fp, "/", 1, dp);
772
if (fgets(state.more.tmp, sizeof(state.more.tmp), stdin)) {
773
if ((m = strlen(state.more.tmp)) > 1 && state.more.tmp[m - 1] == '\n')
774
state.more.tmp[m - 1] = 0;
775
if (state.more.tmp[0])
776
strncopy(state.more.pattern, state.more.tmp, sizeof(state.more.pattern));
777
}
778
}
779
if (state.more.match = strlen(state.more.pattern)) {
780
state.more.row = 1;
781
state.more.col = 1;
782
goto match;
783
}
784
}
785
switch (r) {
786
case '\n':
787
case '\r':
788
state.more.row--;
789
state.more.col = 1;
790
break;
791
case ' ':
792
state.more.row = 2;
793
state.more.col = 1;
794
break;
795
default:
796
state.more.row = 0;
797
return n;
798
}
799
}
800
if (s > b)
801
w += sfwr(fp, b, s - b, dp);
802
return w;
803
}
804
805
#endif
806
807
/*
808
* Trap more variable assignment.
809
*/
810
void
811
set_more(struct var* vp, const char* value)
812
{
813
#if MORE_DISCIPLINE
814
if (state.var.interactive && !state.var.coprocess) {
815
if (value) {
816
if (!state.more.discipline) {
817
state.more.discipline = 1;
818
if (!state.more.init) {
819
state.more.init = 1;
820
state.more.disc.readf = morein;
821
state.more.disc.writef = moreout;
822
}
823
sfdisc(sfstdin, &state.more.disc);
824
sfdisc(sfstdout, &state.more.disc);
825
}
826
}
827
else if (state.more.discipline) {
828
state.more.discipline = 0;
829
sfdisc(sfstdin, NiL);
830
sfdisc(sfstdout, NiL);
831
}
832
moretop();
833
}
834
#else
835
if (value && state.var.interactive && !state.var.coprocess && !state.more.init) {
836
state.more.init = 1;
837
note(0, "Recompile mailx with sfio to enable \"%s\"", vp->name);
838
}
839
#endif
840
}
841
842
/*
843
* clear signal and io state before resuming from interrupt
844
*/
845
846
void
847
resume(int sig)
848
{
849
sigunblock(sig);
850
#if MORE_DISCIPLINE
851
sfclrlock(sfstdin);
852
sfclrlock(sfstdout);
853
sfclrlock(sfstderr);
854
#endif
855
}
856
857