Commit | Line | Data |
---|---|---|
19d73a0e DF |
1 | /* |
2 | * Copyright (c) 1980 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
7 | #ifndef lint | |
65bacefd | 8 | static char *sccsid = "@(#)ex_cmdsub.c 7.9 (Berkeley) %G%"; |
19d73a0e DF |
9 | #endif not lint |
10 | ||
4beab9c4 MH |
11 | #include "ex.h" |
12 | #include "ex_argv.h" | |
13 | #include "ex_temp.h" | |
14 | #include "ex_tty.h" | |
d266c416 | 15 | #include "ex_vis.h" |
4beab9c4 MH |
16 | |
17 | /* | |
18 | * Command mode subroutines implementing | |
19 | * append, args, copy, delete, join, move, put, | |
20 | * shift, tag, yank, z and undo | |
21 | */ | |
22 | ||
23 | bool endline = 1; | |
24 | line *tad1; | |
d266c416 | 25 | static jnoop(); |
4beab9c4 MH |
26 | |
27 | /* | |
28 | * Append after line a lines returned by function f. | |
29 | * Be careful about intermediate states to avoid scramble | |
30 | * if an interrupt comes in. | |
31 | */ | |
32 | append(f, a) | |
33 | int (*f)(); | |
34 | line *a; | |
35 | { | |
36 | register line *a1, *a2, *rdot; | |
37 | int nline; | |
38 | ||
39 | nline = 0; | |
40 | dot = a; | |
887e3e0d | 41 | if(FIXUNDO && !inopen && f!=getsub) { |
4beab9c4 MH |
42 | undap1 = undap2 = dot + 1; |
43 | undkind = UNDCHANGE; | |
44 | } | |
45 | while ((*f)() == 0) { | |
46 | if (truedol >= endcore) { | |
47 | if (morelines() < 0) { | |
887e3e0d | 48 | if (FIXUNDO && f == getsub) { |
4beab9c4 MH |
49 | undap1 = addr1; |
50 | undap2 = addr2 + 1; | |
51 | } | |
52 | error("Out of memory@- too many lines in file"); | |
53 | } | |
54 | } | |
55 | nline++; | |
56 | a1 = truedol + 1; | |
57 | a2 = a1 + 1; | |
58 | dot++; | |
59 | undap2++; | |
60 | dol++; | |
61 | unddol++; | |
62 | truedol++; | |
63 | for (rdot = dot; a1 > rdot;) | |
64 | *--a2 = *--a1; | |
65 | *rdot = 0; | |
66 | putmark(rdot); | |
67 | if (f == gettty) { | |
68 | dirtcnt++; | |
69 | TSYNC(); | |
70 | } | |
71 | } | |
72 | return (nline); | |
73 | } | |
74 | ||
75 | appendnone() | |
76 | { | |
77 | ||
887e3e0d | 78 | if(FIXUNDO) { |
4beab9c4 MH |
79 | undkind = UNDCHANGE; |
80 | undap1 = undap2 = addr1; | |
81 | } | |
82 | } | |
83 | ||
84 | /* | |
85 | * Print out the argument list, with []'s around the current name. | |
86 | */ | |
87 | pargs() | |
88 | { | |
89 | register char **av = argv0, *as = args0; | |
90 | register int ac; | |
91 | ||
92 | for (ac = 0; ac < argc0; ac++) { | |
93 | if (ac != 0) | |
5a6c967e | 94 | ex_putchar(' ' | QUOTE); |
4beab9c4 | 95 | if (ac + argc == argc0 - 1) |
5a6c967e | 96 | ex_printf("["); |
4beab9c4 MH |
97 | lprintf("%s", as); |
98 | if (ac + argc == argc0 - 1) | |
5a6c967e | 99 | ex_printf("]"); |
4beab9c4 MH |
100 | as = av ? *++av : strend(as) + 1; |
101 | } | |
102 | noonl(); | |
103 | } | |
104 | ||
105 | /* | |
106 | * Delete lines; two cases are if we are really deleting, | |
107 | * more commonly we are just moving lines to the undo save area. | |
108 | */ | |
5a6c967e | 109 | ex_delete(hush) |
4beab9c4 MH |
110 | bool hush; |
111 | { | |
112 | register line *a1, *a2; | |
113 | ||
114 | nonzero(); | |
887e3e0d | 115 | if(FIXUNDO) { |
4beab9c4 MH |
116 | register int (*dsavint)(); |
117 | ||
04379bab MH |
118 | #ifdef TRACE |
119 | if (trace) | |
120 | vudump("before delete"); | |
121 | #endif | |
4beab9c4 MH |
122 | change(); |
123 | dsavint = signal(SIGINT, SIG_IGN); | |
124 | undkind = UNDCHANGE; | |
125 | a1 = addr1; | |
126 | squish(); | |
127 | a2 = addr2; | |
128 | if (a2++ != dol) { | |
129 | reverse(a1, a2); | |
130 | reverse(a2, dol + 1); | |
131 | reverse(a1, dol + 1); | |
132 | } | |
133 | dol -= a2 - a1; | |
134 | unddel = a1 - 1; | |
135 | if (a1 > dol) | |
136 | a1 = dol; | |
137 | dot = a1; | |
138 | pkill[0] = pkill[1] = 0; | |
139 | signal(SIGINT, dsavint); | |
04379bab MH |
140 | #ifdef TRACE |
141 | if (trace) | |
142 | vudump("after delete"); | |
143 | #endif | |
4beab9c4 MH |
144 | } else { |
145 | register line *a3; | |
146 | register int i; | |
147 | ||
148 | change(); | |
149 | a1 = addr1; | |
150 | a2 = addr2 + 1; | |
151 | a3 = truedol; | |
152 | i = a2 - a1; | |
153 | unddol -= i; | |
154 | undap2 -= i; | |
155 | dol -= i; | |
156 | truedol -= i; | |
157 | do | |
158 | *a1++ = *a2++; | |
159 | while (a2 <= a3); | |
160 | a1 = addr1; | |
161 | if (a1 > dol) | |
162 | a1 = dol; | |
163 | dot = a1; | |
164 | } | |
165 | if (!hush) | |
166 | killed(); | |
167 | } | |
168 | ||
169 | deletenone() | |
170 | { | |
171 | ||
887e3e0d | 172 | if(FIXUNDO) { |
4beab9c4 MH |
173 | undkind = UNDCHANGE; |
174 | squish(); | |
175 | unddel = addr1; | |
176 | } | |
177 | } | |
178 | ||
179 | /* | |
180 | * Crush out the undo save area, moving the open/visual | |
181 | * save area down in its place. | |
182 | */ | |
183 | squish() | |
184 | { | |
185 | register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1; | |
186 | ||
887e3e0d MH |
187 | if(FIXUNDO) { |
188 | if (inopen == -1) | |
189 | return; | |
190 | if (a1 < a2 && a2 < a3) | |
191 | do | |
192 | *a1++ = *a2++; | |
193 | while (a2 < a3); | |
194 | truedol -= unddol - dol; | |
195 | unddol = dol; | |
196 | } | |
4beab9c4 MH |
197 | } |
198 | ||
199 | /* | |
200 | * Join lines. Special hacks put in spaces, two spaces if | |
201 | * preceding line ends with '.', or no spaces if next line starts with ). | |
202 | */ | |
203 | static int jcount, jnoop(); | |
204 | ||
205 | join(c) | |
206 | int c; | |
207 | { | |
208 | register line *a1; | |
209 | register char *cp, *cp1; | |
210 | ||
211 | cp = genbuf; | |
212 | *cp = 0; | |
213 | for (a1 = addr1; a1 <= addr2; a1++) { | |
214 | getline(*a1); | |
215 | cp1 = linebuf; | |
216 | if (a1 != addr1 && c == 0) { | |
217 | while (*cp1 == ' ' || *cp1 == '\t') | |
218 | cp1++; | |
219 | if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') { | |
220 | if (*cp1 != ')') { | |
221 | *cp++ = ' '; | |
222 | if (cp[-2] == '.') | |
223 | *cp++ = ' '; | |
224 | } | |
225 | } | |
226 | } | |
227 | while (*cp++ = *cp1++) | |
228 | if (cp > &genbuf[LBSIZE-2]) | |
229 | error("Line overflow|Result line of join would be too long"); | |
230 | cp--; | |
231 | } | |
232 | strcLIN(genbuf); | |
5a6c967e | 233 | ex_delete(0); |
4beab9c4 | 234 | jcount = 1; |
d266c416 MH |
235 | if (FIXUNDO) |
236 | undap1 = undap2 = addr1; | |
4beab9c4 | 237 | ignore(append(jnoop, --addr1)); |
d266c416 MH |
238 | if (FIXUNDO) |
239 | vundkind = VMANY; | |
4beab9c4 MH |
240 | } |
241 | ||
242 | static | |
243 | jnoop() | |
244 | { | |
245 | ||
246 | return(--jcount); | |
247 | } | |
248 | ||
249 | /* | |
250 | * Move and copy lines. Hard work is done by move1 which | |
251 | * is also called by undo. | |
252 | */ | |
253 | int getcopy(); | |
254 | ||
255 | move() | |
256 | { | |
257 | register line *adt; | |
258 | bool iscopy = 0; | |
259 | ||
260 | if (Command[0] == 'm') { | |
261 | setdot1(); | |
262 | markpr(addr2 == dot ? addr1 - 1 : addr2 + 1); | |
263 | } else { | |
264 | iscopy++; | |
265 | setdot(); | |
266 | } | |
267 | nonzero(); | |
299f2784 | 268 | adt = address((char*)0); |
4beab9c4 MH |
269 | if (adt == 0) |
270 | serror("%s where?|%s requires a trailing address", Command); | |
271 | newline(); | |
272 | move1(iscopy, adt); | |
273 | killed(); | |
274 | } | |
275 | ||
276 | move1(cflag, addrt) | |
277 | int cflag; | |
278 | line *addrt; | |
279 | { | |
280 | register line *adt, *ad1, *ad2; | |
281 | int lines; | |
282 | ||
283 | adt = addrt; | |
284 | lines = (addr2 - addr1) + 1; | |
285 | if (cflag) { | |
286 | tad1 = addr1; | |
287 | ad1 = dol; | |
288 | ignore(append(getcopy, ad1++)); | |
289 | ad2 = dol; | |
290 | } else { | |
291 | ad2 = addr2; | |
292 | for (ad1 = addr1; ad1 <= ad2;) | |
293 | *ad1++ &= ~01; | |
294 | ad1 = addr1; | |
295 | } | |
296 | ad2++; | |
297 | if (adt < ad1) { | |
298 | if (adt + 1 == ad1 && !cflag && !inglobal) | |
299 | error("That move would do nothing!"); | |
300 | dot = adt + (ad2 - ad1); | |
301 | if (++adt != ad1) { | |
302 | reverse(adt, ad1); | |
303 | reverse(ad1, ad2); | |
304 | reverse(adt, ad2); | |
305 | } | |
306 | } else if (adt >= ad2) { | |
307 | dot = adt++; | |
308 | reverse(ad1, ad2); | |
309 | reverse(ad2, adt); | |
310 | reverse(ad1, adt); | |
311 | } else | |
312 | error("Move to a moved line"); | |
313 | change(); | |
314 | if (!inglobal) | |
887e3e0d MH |
315 | if(FIXUNDO) { |
316 | if (cflag) { | |
317 | undap1 = addrt + 1; | |
318 | undap2 = undap1 + lines; | |
319 | deletenone(); | |
320 | } else { | |
321 | undkind = UNDMOVE; | |
322 | undap1 = addr1; | |
323 | undap2 = addr2; | |
324 | unddel = addrt; | |
325 | squish(); | |
326 | } | |
4beab9c4 MH |
327 | } |
328 | } | |
329 | ||
330 | getcopy() | |
331 | { | |
332 | ||
333 | if (tad1 > addr2) | |
334 | return (EOF); | |
335 | getline(*tad1++); | |
336 | return (0); | |
337 | } | |
338 | ||
339 | /* | |
340 | * Put lines in the buffer from the undo save area. | |
341 | */ | |
342 | getput() | |
343 | { | |
344 | ||
345 | if (tad1 > unddol) | |
346 | return (EOF); | |
347 | getline(*tad1++); | |
348 | tad1++; | |
349 | return (0); | |
350 | } | |
351 | ||
352 | put() | |
353 | { | |
354 | register int cnt; | |
355 | ||
887e3e0d MH |
356 | if (!FIXUNDO) |
357 | error("Cannot put inside global/macro"); | |
4beab9c4 MH |
358 | cnt = unddol - dol; |
359 | if (cnt && inopen && pkill[0] && pkill[1]) { | |
360 | pragged(1); | |
361 | return; | |
362 | } | |
363 | tad1 = dol + 1; | |
364 | ignore(append(getput, addr2)); | |
365 | undkind = UNDPUT; | |
366 | notecnt = cnt; | |
367 | netchange(cnt); | |
368 | } | |
369 | ||
370 | /* | |
371 | * A tricky put, of a group of lines in the middle | |
372 | * of an existing line. Only from open/visual. | |
373 | * Argument says pkills have meaning, e.g. called from | |
374 | * put; it is 0 on calls from putreg. | |
375 | */ | |
376 | pragged(kill) | |
377 | bool kill; | |
378 | { | |
379 | extern char *cursor; | |
380 | register char *gp = &genbuf[cursor - linebuf]; | |
381 | ||
382 | /* | |
383 | * This kind of stuff is TECO's forte. | |
384 | * We just grunge along, since it cuts | |
385 | * across our line-oriented model of the world | |
386 | * almost scrambling our addled brain. | |
387 | */ | |
388 | if (!kill) | |
389 | getDOT(); | |
390 | strcpy(genbuf, linebuf); | |
391 | getline(*unddol); | |
392 | if (kill) | |
393 | *pkill[1] = 0; | |
394 | strcat(linebuf, gp); | |
395 | putmark(unddol); | |
396 | getline(dol[1]); | |
397 | if (kill) | |
398 | strcLIN(pkill[0]); | |
399 | strcpy(gp, linebuf); | |
400 | strcLIN(genbuf); | |
401 | putmark(dol+1); | |
402 | undkind = UNDCHANGE; | |
403 | undap1 = dot; | |
404 | undap2 = dot + 1; | |
405 | unddel = dot - 1; | |
406 | undo(1); | |
407 | } | |
408 | ||
409 | /* | |
410 | * Shift lines, based on c. | |
411 | * If c is neither < nor >, then this is a lisp aligning =. | |
412 | */ | |
413 | shift(c, cnt) | |
414 | int c; | |
415 | int cnt; | |
416 | { | |
417 | register line *addr; | |
418 | register char *cp; | |
419 | char *dp; | |
420 | register int i; | |
421 | ||
887e3e0d | 422 | if(FIXUNDO) |
4beab9c4 MH |
423 | save12(), undkind = UNDCHANGE; |
424 | cnt *= value(SHIFTWIDTH); | |
425 | for (addr = addr1; addr <= addr2; addr++) { | |
426 | dot = addr; | |
427 | #ifdef LISPCODE | |
428 | if (c == '=' && addr == addr1 && addr != addr2) | |
429 | continue; | |
430 | #endif | |
431 | getDOT(); | |
432 | i = whitecnt(linebuf); | |
433 | switch (c) { | |
434 | ||
435 | case '>': | |
436 | if (linebuf[0] == 0) | |
437 | continue; | |
438 | cp = genindent(i + cnt); | |
439 | break; | |
440 | ||
441 | case '<': | |
442 | if (i == 0) | |
443 | continue; | |
444 | i -= cnt; | |
445 | cp = i > 0 ? genindent(i) : genbuf; | |
446 | break; | |
447 | ||
448 | #ifdef LISPCODE | |
449 | default: | |
450 | i = lindent(addr); | |
451 | getDOT(); | |
452 | cp = genindent(i); | |
453 | break; | |
454 | #endif | |
455 | } | |
456 | if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2]) | |
457 | error("Line too long|Result line after shift would be too long"); | |
458 | CP(cp, dp); | |
459 | strcLIN(genbuf); | |
460 | putmark(addr); | |
461 | } | |
462 | killed(); | |
463 | } | |
464 | ||
465 | /* | |
466 | * Find a tag in the tags file. | |
467 | * Most work here is in parsing the tags file itself. | |
468 | */ | |
469 | tagfind(quick) | |
470 | bool quick; | |
471 | { | |
472 | char cmdbuf[BUFSIZ]; | |
473 | char filebuf[FNSIZE]; | |
887e3e0d | 474 | char tagfbuf[128]; |
4beab9c4 MH |
475 | register int c, d; |
476 | bool samef = 1; | |
887e3e0d MH |
477 | int tfcount = 0; |
478 | int omagic; | |
479 | char *fn, *fne; | |
43d18593 | 480 | struct stat sbuf; |
6df584e5 JB |
481 | #ifdef FASTTAG |
482 | int iof; | |
483 | char iofbuf[MAXBSIZE]; | |
887e3e0d MH |
484 | long mid; /* assumed byte offset */ |
485 | long top, bot; /* length of tag file */ | |
887e3e0d | 486 | #endif |
4beab9c4 MH |
487 | |
488 | omagic = value(MAGIC); | |
4beab9c4 MH |
489 | if (!skipend()) { |
490 | register char *lp = lasttag; | |
491 | ||
492 | while (!iswhite(peekchar()) && !endcmd(peekchar())) | |
493 | if (lp < &lasttag[sizeof lasttag - 2]) | |
5a6c967e | 494 | *lp++ = ex_getchar(); |
4beab9c4 MH |
495 | else |
496 | ignchar(); | |
497 | *lp++ = 0; | |
498 | if (!endcmd(peekchar())) | |
499 | badtag: | |
500 | error("Bad tag|Give one tag per line"); | |
501 | } else if (lasttag[0] == 0) | |
502 | error("No previous tag"); | |
5a6c967e | 503 | c = ex_getchar(); |
4beab9c4 MH |
504 | if (!endcmd(c)) |
505 | goto badtag; | |
506 | if (c == EOF) | |
507 | ungetchar(c); | |
508 | clrstats(); | |
887e3e0d MH |
509 | |
510 | /* | |
511 | * Loop once for each file in tags "path". | |
512 | */ | |
513 | CP(tagfbuf, svalue(TAGS)); | |
514 | fne = tagfbuf - 1; | |
515 | while (fne) { | |
516 | fn = ++fne; | |
517 | while (*fne && *fne != ' ') | |
518 | fne++; | |
519 | if (*fne == 0) | |
520 | fne = 0; /* done, quit after this time */ | |
521 | else | |
522 | *fne = 0; /* null terminate filename */ | |
6df584e5 JB |
523 | #ifdef FASTTAG |
524 | iof = topen(fn, iofbuf); | |
525 | if (iof == -1) | |
887e3e0d MH |
526 | continue; |
527 | tfcount++; | |
6df584e5 | 528 | fstat(iof, &sbuf); |
887e3e0d | 529 | top = sbuf.st_size; |
6df584e5 | 530 | if (top == 0L ) |
887e3e0d MH |
531 | top = -1L; |
532 | bot = 0L; | |
533 | while (top >= bot) { | |
534 | #else | |
535 | /* | |
536 | * Avoid stdio and scan tag file linearly. | |
537 | */ | |
538 | io = open(fn, 0); | |
539 | if (io<0) | |
540 | continue; | |
d266c416 | 541 | tfcount++; |
43d18593 JB |
542 | if (fstat(io, &sbuf) < 0) |
543 | bsize = LBSIZE; | |
544 | else { | |
545 | bsize = sbuf.st_blksize; | |
546 | if (bsize <= 0) | |
547 | bsize = LBSIZE; | |
548 | } | |
4beab9c4 | 549 | while (getfile() == 0) { |
887e3e0d MH |
550 | #endif |
551 | /* loop for each tags file entry */ | |
4beab9c4 MH |
552 | register char *cp = linebuf; |
553 | register char *lp = lasttag; | |
554 | char *oglobp; | |
555 | ||
6df584e5 | 556 | #ifdef FASTTAG |
887e3e0d | 557 | mid = (top + bot) / 2; |
6df584e5 | 558 | tseek(iof, mid); |
887e3e0d | 559 | if (mid > 0) /* to get first tag in file to work */ |
04379bab | 560 | /* scan to next \n */ |
6df584e5 | 561 | if(tgets(linebuf, sizeof linebuf, iof)==NULL) |
04379bab MH |
562 | goto goleft; |
563 | /* get the line itself */ | |
6df584e5 | 564 | if(tgets(linebuf, sizeof linebuf, iof)==NULL) |
04379bab | 565 | goto goleft; |
6df584e5 | 566 | #ifdef TDEBUG |
5a6c967e | 567 | ex_printf("tag: %o %o %o %s\n", bot, mid, top, linebuf); |
6df584e5 | 568 | #endif |
887e3e0d | 569 | #endif |
4beab9c4 MH |
570 | while (*cp && *lp == *cp) |
571 | cp++, lp++; | |
6df584e5 JB |
572 | if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 || |
573 | lp-lasttag < value(TAGLENGTH))) { | |
574 | #ifdef FASTTAG | |
887e3e0d MH |
575 | if (*lp > *cp) |
576 | bot = mid + 1; | |
577 | else | |
04379bab | 578 | goleft: |
887e3e0d MH |
579 | top = mid - 1; |
580 | #endif | |
581 | /* Not this tag. Try the next */ | |
4beab9c4 | 582 | continue; |
887e3e0d MH |
583 | } |
584 | ||
585 | /* | |
586 | * We found the tag. Decode the line in the file. | |
587 | */ | |
6df584e5 JB |
588 | #ifdef FASTTAG |
589 | tclose(iof); | |
887e3e0d | 590 | #else |
4beab9c4 | 591 | close(io); |
887e3e0d | 592 | #endif |
d266c416 MH |
593 | /* Rest of tag if abbreviated */ |
594 | while (!iswhite(*cp)) | |
595 | cp++; | |
596 | ||
887e3e0d | 597 | /* name of file */ |
4beab9c4 MH |
598 | while (*cp && iswhite(*cp)) |
599 | cp++; | |
600 | if (!*cp) | |
601 | badtags: | |
602 | serror("%s: Bad tags file entry", lasttag); | |
603 | lp = filebuf; | |
604 | while (*cp && *cp != ' ' && *cp != '\t') { | |
605 | if (lp < &filebuf[sizeof filebuf - 2]) | |
606 | *lp++ = *cp; | |
607 | cp++; | |
608 | } | |
609 | *lp++ = 0; | |
887e3e0d | 610 | |
4beab9c4 MH |
611 | if (*cp == 0) |
612 | goto badtags; | |
613 | if (dol != zero) { | |
614 | /* | |
615 | * Save current position in 't for ^^ in visual. | |
616 | */ | |
617 | names['t'-'a'] = *dot &~ 01; | |
618 | if (inopen) { | |
d266c416 | 619 | extern char *ncols['z'-'a'+2]; |
4beab9c4 MH |
620 | extern char *cursor; |
621 | ||
622 | ncols['t'-'a'] = cursor; | |
623 | } | |
624 | } | |
625 | strcpy(cmdbuf, cp); | |
626 | if (strcmp(filebuf, savedfile) || !edited) { | |
627 | char cmdbuf2[sizeof filebuf + 10]; | |
628 | ||
887e3e0d | 629 | /* Different file. Do autowrite & get it. */ |
4beab9c4 MH |
630 | if (!quick) { |
631 | ckaw(); | |
632 | if (chng && dol > zero) | |
633 | error("No write@since last change (:tag! overrides)"); | |
634 | } | |
635 | oglobp = globp; | |
636 | strcpy(cmdbuf2, "e! "); | |
637 | strcat(cmdbuf2, filebuf); | |
638 | globp = cmdbuf2; | |
639 | d = peekc; ungetchar(0); | |
640 | commands(1, 1); | |
641 | peekc = d; | |
642 | globp = oglobp; | |
44232d5b | 643 | value(MAGIC) = omagic; |
4beab9c4 MH |
644 | samef = 0; |
645 | } | |
887e3e0d MH |
646 | |
647 | /* | |
648 | * Look for pattern in the current file. | |
649 | */ | |
4beab9c4 MH |
650 | oglobp = globp; |
651 | globp = cmdbuf; | |
652 | d = peekc; ungetchar(0); | |
653 | if (samef) | |
654 | markpr(dot); | |
887e3e0d MH |
655 | /* |
656 | * BUG: if it isn't found (user edited header | |
657 | * line) we get left in nomagic mode. | |
658 | */ | |
44232d5b | 659 | value(MAGIC) = 0; |
4beab9c4 MH |
660 | commands(1, 1); |
661 | peekc = d; | |
662 | globp = oglobp; | |
663 | value(MAGIC) = omagic; | |
664 | return; | |
887e3e0d MH |
665 | } /* end of "for each tag in file" */ |
666 | ||
667 | /* | |
668 | * No such tag in this file. Close it and try the next. | |
669 | */ | |
6df584e5 JB |
670 | #ifdef FASTTAG |
671 | tclose(iof); | |
887e3e0d MH |
672 | #else |
673 | close(io); | |
674 | #endif | |
675 | } /* end of "for each file in path" */ | |
676 | if (tfcount <= 0) | |
4beab9c4 | 677 | error("No tags file"); |
887e3e0d MH |
678 | else |
679 | serror("%s: No such tag@in tags file", lasttag); | |
4beab9c4 MH |
680 | } |
681 | ||
682 | /* | |
683 | * Save lines from addr1 thru addr2 as though | |
684 | * they had been deleted. | |
685 | */ | |
686 | yank() | |
687 | { | |
688 | ||
887e3e0d MH |
689 | if (!FIXUNDO) |
690 | error("Can't yank inside global/macro"); | |
4beab9c4 MH |
691 | save12(); |
692 | undkind = UNDNONE; | |
693 | killcnt(addr2 - addr1 + 1); | |
694 | } | |
695 | ||
696 | /* | |
697 | * z command; print windows of text in the file. | |
698 | * | |
699 | * If this seems unreasonably arcane, the reasons | |
700 | * are historical. This is one of the first commands | |
701 | * added to the first ex (then called en) and the | |
702 | * number of facilities here were the major advantage | |
703 | * of en over ed since they allowed more use to be | |
704 | * made of fast terminals w/o typing .,.22p all the time. | |
705 | */ | |
706 | bool zhadpr; | |
707 | bool znoclear; | |
708 | short zweight; | |
709 | ||
710 | zop(hadpr) | |
711 | int hadpr; | |
712 | { | |
713 | register int c, lines, op; | |
714 | bool excl; | |
715 | ||
716 | zhadpr = hadpr; | |
717 | notempty(); | |
718 | znoclear = 0; | |
719 | zweight = 0; | |
720 | excl = exclam(); | |
5a6c967e | 721 | switch (c = op = ex_getchar()) { |
4beab9c4 MH |
722 | |
723 | case '^': | |
724 | zweight = 1; | |
725 | case '-': | |
726 | case '+': | |
727 | while (peekchar() == op) { | |
728 | ignchar(); | |
729 | zweight++; | |
730 | } | |
731 | case '=': | |
732 | case '.': | |
5a6c967e | 733 | c = ex_getchar(); |
4beab9c4 MH |
734 | break; |
735 | ||
736 | case EOF: | |
737 | znoclear++; | |
738 | break; | |
739 | ||
740 | default: | |
741 | op = 0; | |
742 | break; | |
743 | } | |
744 | if (isdigit(c)) { | |
745 | lines = c - '0'; | |
746 | for(;;) { | |
5a6c967e | 747 | c = ex_getchar(); |
4beab9c4 MH |
748 | if (!isdigit(c)) |
749 | break; | |
750 | lines *= 10; | |
751 | lines += c - '0'; | |
752 | } | |
753 | if (lines < LINES) | |
754 | znoclear++; | |
755 | value(WINDOW) = lines; | |
756 | if (op == '=') | |
757 | lines += 2; | |
758 | } else | |
759 | lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL); | |
760 | if (inopen || c != EOF) { | |
761 | ungetchar(c); | |
762 | newline(); | |
763 | } | |
764 | addr1 = addr2; | |
765 | if (addr2 == 0 && dot < dol && op == 0) | |
766 | addr1 = addr2 = dot+1; | |
767 | setdot(); | |
768 | zop2(lines, op); | |
769 | } | |
770 | ||
771 | zop2(lines, op) | |
772 | register int lines; | |
773 | register int op; | |
774 | { | |
775 | register line *split; | |
776 | ||
777 | split = NULL; | |
778 | switch (op) { | |
779 | ||
780 | case EOF: | |
781 | if (addr2 == dol) | |
782 | error("\nAt EOF"); | |
783 | case '+': | |
784 | if (addr2 == dol) | |
785 | error("At EOF"); | |
786 | addr2 += lines * zweight; | |
787 | if (addr2 > dol) | |
788 | error("Hit BOTTOM"); | |
789 | addr2++; | |
790 | default: | |
791 | addr1 = addr2; | |
792 | addr2 += lines-1; | |
793 | dot = addr2; | |
794 | break; | |
795 | ||
796 | case '=': | |
797 | case '.': | |
798 | znoclear = 0; | |
799 | lines--; | |
800 | lines >>= 1; | |
801 | if (op == '=') | |
802 | lines--; | |
803 | addr1 = addr2 - lines; | |
804 | if (op == '=') | |
805 | dot = split = addr2; | |
806 | addr2 += lines; | |
807 | if (op == '.') { | |
808 | markDOT(); | |
809 | dot = addr2; | |
810 | } | |
811 | break; | |
812 | ||
813 | case '^': | |
814 | case '-': | |
815 | addr2 -= lines * zweight; | |
816 | if (addr2 < one) | |
817 | error("Hit TOP"); | |
818 | lines--; | |
819 | addr1 = addr2 - lines; | |
820 | dot = addr2; | |
821 | break; | |
822 | } | |
823 | if (addr1 <= zero) | |
824 | addr1 = one; | |
825 | if (addr2 > dol) | |
826 | addr2 = dol; | |
827 | if (dot > dol) | |
828 | dot = dol; | |
829 | if (addr1 > addr2) | |
830 | return; | |
831 | if (op == EOF && zhadpr) { | |
832 | getline(*addr1); | |
5a6c967e | 833 | ex_putchar('\r' | QUOTE); |
4beab9c4 MH |
834 | shudclob = 1; |
835 | } else if (znoclear == 0 && CL != NOSTR && !inopen) { | |
836 | flush1(); | |
837 | vclear(); | |
838 | } | |
839 | if (addr2 - addr1 > 1) | |
840 | pstart(); | |
841 | if (split) { | |
842 | plines(addr1, split - 1, 0); | |
843 | splitit(); | |
844 | plines(split, split, 0); | |
845 | splitit(); | |
846 | addr1 = split + 1; | |
847 | } | |
848 | plines(addr1, addr2, 0); | |
849 | } | |
850 | ||
851 | static | |
852 | splitit() | |
853 | { | |
854 | register int l; | |
855 | ||
856 | for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--) | |
5a6c967e | 857 | ex_putchar('-'); |
4beab9c4 MH |
858 | putnl(); |
859 | } | |
860 | ||
861 | plines(adr1, adr2, movedot) | |
862 | line *adr1; | |
863 | register line *adr2; | |
864 | bool movedot; | |
865 | { | |
866 | register line *addr; | |
867 | ||
868 | pofix(); | |
869 | for (addr = adr1; addr <= adr2; addr++) { | |
870 | getline(*addr); | |
871 | pline(lineno(addr)); | |
872 | if (inopen) | |
5a6c967e | 873 | ex_putchar('\n' | QUOTE); |
4beab9c4 MH |
874 | if (movedot) |
875 | dot = addr; | |
876 | } | |
877 | } | |
878 | ||
879 | pofix() | |
880 | { | |
881 | ||
882 | if (inopen && Outchar != termchar) { | |
883 | vnfl(); | |
884 | setoutt(); | |
885 | } | |
886 | } | |
887 | ||
888 | /* | |
889 | * Dudley doright to the rescue. | |
890 | * Undo saves the day again. | |
891 | * A tip of the hatlo hat to Warren Teitleman | |
892 | * who made undo as useful as do. | |
893 | * | |
894 | * Command level undo works easily because | |
895 | * the editor has a unique temporary file | |
896 | * index for every line which ever existed. | |
897 | * We don't have to save large blocks of text, | |
898 | * only the indices which are small. We do this | |
899 | * by moving them to after the last line in the | |
900 | * line buffer array, and marking down info | |
901 | * about whence they came. | |
902 | * | |
903 | * Undo is its own inverse. | |
904 | */ | |
905 | undo(c) | |
906 | bool c; | |
907 | { | |
908 | register int i; | |
909 | register line *jp, *kp; | |
910 | line *dolp1, *newdol, *newadot; | |
911 | ||
887e3e0d MH |
912 | #ifdef TRACE |
913 | if (trace) | |
914 | vudump("before undo"); | |
915 | #endif | |
4beab9c4 MH |
916 | if (inglobal && inopen <= 0) |
917 | error("Can't undo in global@commands"); | |
918 | if (!c) | |
919 | somechange(); | |
920 | pkill[0] = pkill[1] = 0; | |
921 | change(); | |
922 | if (undkind == UNDMOVE) { | |
923 | /* | |
924 | * Command to be undone is a move command. | |
925 | * This is handled as a special case by noting that | |
926 | * a move "a,b m c" can be inverted by another move. | |
927 | */ | |
928 | if ((i = (jp = unddel) - undap2) > 0) { | |
929 | /* | |
930 | * when c > b inverse is a+(c-b),c m a-1 | |
931 | */ | |
932 | addr2 = jp; | |
933 | addr1 = (jp = undap1) + i; | |
934 | unddel = jp-1; | |
935 | } else { | |
936 | /* | |
937 | * when b > c inverse is c+1,c+1+(b-a) m b | |
938 | */ | |
939 | addr1 = ++jp; | |
940 | addr2 = jp + ((unddel = undap2) - undap1); | |
941 | } | |
942 | kp = undap1; | |
943 | move1(0, unddel); | |
944 | dot = kp; | |
945 | Command = "move"; | |
946 | killed(); | |
947 | } else { | |
948 | int cnt; | |
949 | ||
950 | newadot = dot; | |
951 | cnt = lineDOL(); | |
952 | newdol = dol; | |
953 | dolp1 = dol + 1; | |
954 | /* | |
955 | * Command to be undone is a non-move. | |
956 | * All such commands are treated as a combination of | |
957 | * a delete command and a append command. | |
958 | * We first move the lines appended by the last command | |
959 | * from undap1 to undap2-1 so that they are just before the | |
960 | * saved deleted lines. | |
961 | */ | |
962 | if ((i = (kp = undap2) - (jp = undap1)) > 0) { | |
963 | if (kp != dolp1) { | |
964 | reverse(jp, kp); | |
965 | reverse(kp, dolp1); | |
966 | reverse(jp, dolp1); | |
967 | } | |
968 | /* | |
969 | * Account for possible backward motion of target | |
970 | * for restoration of saved deleted lines. | |
971 | */ | |
972 | if (unddel >= jp) | |
973 | unddel -= i; | |
974 | newdol -= i; | |
975 | /* | |
976 | * For the case where no lines are restored, dot | |
977 | * is the line before the first line deleted. | |
978 | */ | |
979 | dot = jp-1; | |
980 | } | |
981 | /* | |
982 | * Now put the deleted lines, if any, back where they were. | |
983 | * Basic operation is: dol+1,unddol m unddel | |
984 | */ | |
985 | if (undkind == UNDPUT) { | |
986 | unddel = undap1 - 1; | |
987 | squish(); | |
988 | } | |
989 | jp = unddel + 1; | |
990 | if ((i = (kp = unddol) - dol) > 0) { | |
991 | if (jp != dolp1) { | |
992 | reverse(jp, dolp1); | |
993 | reverse(dolp1, ++kp); | |
994 | reverse(jp, kp); | |
995 | } | |
996 | /* | |
997 | * Account for possible forward motion of the target | |
998 | * for restoration of the deleted lines. | |
999 | */ | |
1000 | if (undap1 >= jp) | |
1001 | undap1 += i; | |
1002 | /* | |
1003 | * Dot is the first resurrected line. | |
1004 | */ | |
1005 | dot = jp; | |
1006 | newdol += i; | |
1007 | } | |
1008 | /* | |
1009 | * Clean up so we are invertible | |
1010 | */ | |
1011 | unddel = undap1 - 1; | |
1012 | undap1 = jp; | |
1013 | undap2 = jp + i; | |
1014 | dol = newdol; | |
1015 | netchHAD(cnt); | |
1016 | if (undkind == UNDALL) { | |
1017 | dot = undadot; | |
1018 | undadot = newadot; | |
887e3e0d MH |
1019 | } else |
1020 | undkind = UNDCHANGE; | |
4beab9c4 | 1021 | } |
d266c416 MH |
1022 | /* |
1023 | * Defensive programming - after a munged undadot. | |
1024 | * Also handle empty buffer case. | |
1025 | */ | |
1026 | if ((dot <= zero || dot > dol) && dot != dol) | |
4beab9c4 | 1027 | dot = one; |
887e3e0d MH |
1028 | #ifdef TRACE |
1029 | if (trace) | |
1030 | vudump("after undo"); | |
1031 | #endif | |
4beab9c4 MH |
1032 | } |
1033 | ||
1034 | /* | |
1035 | * Be (almost completely) sure there really | |
1036 | * was a change, before claiming to undo. | |
1037 | */ | |
1038 | somechange() | |
1039 | { | |
1040 | register line *ip, *jp; | |
1041 | ||
1042 | switch (undkind) { | |
1043 | ||
1044 | case UNDMOVE: | |
1045 | return; | |
1046 | ||
1047 | case UNDCHANGE: | |
1048 | if (undap1 == undap2 && dol == unddol) | |
1049 | break; | |
1050 | return; | |
1051 | ||
1052 | case UNDPUT: | |
1053 | if (undap1 != undap2) | |
1054 | return; | |
1055 | break; | |
1056 | ||
1057 | case UNDALL: | |
1058 | if (unddol - dol != lineDOL()) | |
1059 | return; | |
1060 | for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++) | |
1061 | if ((*ip &~ 01) != (*jp &~ 01)) | |
1062 | return; | |
1063 | break; | |
1064 | ||
1065 | case UNDNONE: | |
1066 | error("Nothing to undo"); | |
1067 | } | |
1068 | error("Nothing changed|Last undoable command didn't change anything"); | |
1069 | } | |
1070 | ||
1071 | /* | |
1072 | * Map command: | |
1073 | * map src dest | |
1074 | */ | |
d266c416 | 1075 | mapcmd(un, ab) |
4beab9c4 | 1076 | int un; /* true if this is unmap command */ |
d266c416 | 1077 | int ab; /* true if this is abbr command */ |
4beab9c4 | 1078 | { |
d266c416 | 1079 | char lhs[100], rhs[100]; /* max sizes resp. */ |
4beab9c4 | 1080 | register char *p; |
299f2784 | 1081 | register int c; /* mjm: char --> int */ |
4beab9c4 | 1082 | char *dname; |
887e3e0d | 1083 | struct maps *mp; /* the map structure we are working on */ |
4beab9c4 | 1084 | |
d266c416 | 1085 | mp = ab ? abbrevs : exclam() ? immacs : arrows; |
4beab9c4 MH |
1086 | if (skipend()) { |
1087 | int i; | |
1088 | ||
1089 | /* print current mapping values */ | |
1090 | if (peekchar() != EOF) | |
1091 | ignchar(); | |
d266c416 MH |
1092 | if (un) |
1093 | error("Missing lhs"); | |
4beab9c4 MH |
1094 | if (inopen) |
1095 | pofix(); | |
887e3e0d MH |
1096 | for (i=0; mp[i].mapto; i++) |
1097 | if (mp[i].cap) { | |
1098 | lprintf("%s", mp[i].descr); | |
5a6c967e | 1099 | ex_putchar('\t'); |
887e3e0d | 1100 | lprintf("%s", mp[i].cap); |
5a6c967e | 1101 | ex_putchar('\t'); |
887e3e0d | 1102 | lprintf("%s", mp[i].mapto); |
4beab9c4 MH |
1103 | putNFL(); |
1104 | } | |
1105 | return; | |
1106 | } | |
1107 | ||
1108 | ignore(skipwh()); | |
1109 | for (p=lhs; ; ) { | |
5a6c967e | 1110 | c = ex_getchar(); |
65bacefd | 1111 | if (c == CTRL('v')) { |
5a6c967e | 1112 | c = ex_getchar(); |
d266c416 MH |
1113 | } else if (!un && any(c, " \t")) { |
1114 | /* End of lhs */ | |
1115 | break; | |
1116 | } else if (endcmd(c) && c!='"') { | |
4beab9c4 MH |
1117 | ungetchar(c); |
1118 | if (un) { | |
1119 | newline(); | |
d266c416 | 1120 | *p = 0; |
887e3e0d | 1121 | addmac(lhs, NOSTR, NOSTR, mp); |
4beab9c4 MH |
1122 | return; |
1123 | } else | |
1124 | error("Missing rhs"); | |
1125 | } | |
1126 | *p++ = c; | |
1127 | } | |
1128 | *p = 0; | |
1129 | ||
1130 | if (skipend()) | |
1131 | error("Missing rhs"); | |
1132 | for (p=rhs; ; ) { | |
5a6c967e | 1133 | c = ex_getchar(); |
65bacefd | 1134 | if (c == CTRL('v')) { |
5a6c967e | 1135 | c = ex_getchar(); |
d266c416 | 1136 | } else if (endcmd(c) && c!='"') { |
4beab9c4 MH |
1137 | ungetchar(c); |
1138 | break; | |
1139 | } | |
1140 | *p++ = c; | |
1141 | } | |
1142 | *p = 0; | |
1143 | newline(); | |
1144 | /* | |
1145 | * Special hack for function keys: #1 means key f1, etc. | |
1146 | * If the terminal doesn't have function keys, we just use #1. | |
1147 | */ | |
1148 | if (lhs[0] == '#') { | |
1149 | char *fnkey; | |
1150 | char *fkey(); | |
1151 | char funkey[3]; | |
1152 | ||
1153 | fnkey = fkey(lhs[1] - '0'); | |
1154 | funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0; | |
1155 | if (fnkey) | |
1156 | strcpy(lhs, fnkey); | |
1157 | dname = funkey; | |
1158 | } else { | |
1159 | dname = lhs; | |
1160 | } | |
887e3e0d | 1161 | addmac(lhs,rhs,dname,mp); |
4beab9c4 MH |
1162 | } |
1163 | ||
1164 | /* | |
1165 | * Add a macro definition to those that already exist. The sequence of | |
1166 | * chars "src" is mapped into "dest". If src is already mapped into something | |
1167 | * this overrides the mapping. There is no recursion. Unmap is done by | |
887e3e0d MH |
1168 | * using NOSTR for dest. Dname is what to show in listings. mp is |
1169 | * the structure to affect (arrows, etc). | |
4beab9c4 | 1170 | */ |
887e3e0d | 1171 | addmac(src,dest,dname,mp) |
4beab9c4 | 1172 | register char *src, *dest, *dname; |
887e3e0d | 1173 | register struct maps *mp; |
4beab9c4 MH |
1174 | { |
1175 | register int slot, zer; | |
1176 | ||
d266c416 MH |
1177 | #ifdef TRACE |
1178 | if (trace) | |
1179 | fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp); | |
1180 | #endif | |
887e3e0d | 1181 | if (dest && mp==arrows) { |
4beab9c4 MH |
1182 | /* Make sure user doesn't screw himself */ |
1183 | /* | |
44232d5b MH |
1184 | * Prevent tail recursion. We really should be |
1185 | * checking to see if src is a suffix of dest | |
887e3e0d MH |
1186 | * but this makes mapping involving escapes that |
1187 | * is reasonable mess up. | |
4beab9c4 | 1188 | */ |
44232d5b MH |
1189 | if (src[1] == 0 && src[0] == dest[strlen(dest)-1]) |
1190 | error("No tail recursion"); | |
4beab9c4 MH |
1191 | /* |
1192 | * We don't let the user rob himself of ":", and making | |
1193 | * multi char words is a bad idea so we don't allow it. | |
1194 | * Note that if user sets mapinput and maps all of return, | |
1195 | * linefeed, and escape, he can screw himself. This is | |
1196 | * so weird I don't bother to check for it. | |
1197 | */ | |
1198 | if (isalpha(src[0]) && src[1] || any(src[0],":")) | |
1199 | error("Too dangerous to map that"); | |
4beab9c4 | 1200 | } |
887e3e0d MH |
1201 | else if (dest) { |
1202 | /* check for tail recursion in input mode: fussier */ | |
1203 | if (eq(src, dest+strlen(dest)-strlen(src))) | |
1204 | error("No tail recursion"); | |
1205 | } | |
1206 | /* | |
1207 | * If the src were null it would cause the dest to | |
1208 | * be mapped always forever. This is not good. | |
1209 | */ | |
1210 | if (src == NOSTR || src[0] == 0) | |
1211 | error("Missing lhs"); | |
4beab9c4 MH |
1212 | |
1213 | /* see if we already have a def for src */ | |
1214 | zer = -1; | |
887e3e0d MH |
1215 | for (slot=0; mp[slot].mapto; slot++) { |
1216 | if (mp[slot].cap) { | |
d266c416 | 1217 | if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto)) |
4beab9c4 MH |
1218 | break; /* if so, reuse slot */ |
1219 | } else { | |
1220 | zer = slot; /* remember an empty slot */ | |
1221 | } | |
1222 | } | |
1223 | ||
1224 | if (dest == NOSTR) { | |
1225 | /* unmap */ | |
887e3e0d MH |
1226 | if (mp[slot].cap) { |
1227 | mp[slot].cap = NOSTR; | |
1228 | mp[slot].descr = NOSTR; | |
4beab9c4 MH |
1229 | } else { |
1230 | error("Not mapped|That macro wasn't mapped"); | |
1231 | } | |
1232 | return; | |
1233 | } | |
1234 | ||
1235 | /* reuse empty slot, if we found one and src isn't already defined */ | |
887e3e0d | 1236 | if (zer >= 0 && mp[slot].mapto == 0) |
4beab9c4 MH |
1237 | slot = zer; |
1238 | ||
1239 | /* if not, append to end */ | |
1240 | if (slot >= MAXNOMACS) | |
1241 | error("Too many macros"); | |
1242 | if (msnext == 0) /* first time */ | |
1243 | msnext = mapspace; | |
1244 | /* Check is a bit conservative, we charge for dname even if reusing src */ | |
1245 | if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS) | |
1246 | error("Too much macro text"); | |
1247 | CP(msnext, src); | |
887e3e0d | 1248 | mp[slot].cap = msnext; |
4beab9c4 MH |
1249 | msnext += strlen(src) + 1; /* plus 1 for null on the end */ |
1250 | CP(msnext, dest); | |
887e3e0d | 1251 | mp[slot].mapto = msnext; |
4beab9c4 MH |
1252 | msnext += strlen(dest) + 1; |
1253 | if (dname) { | |
1254 | CP(msnext, dname); | |
887e3e0d | 1255 | mp[slot].descr = msnext; |
4beab9c4 MH |
1256 | msnext += strlen(dname) + 1; |
1257 | } else { | |
1258 | /* default descr to string user enters */ | |
887e3e0d | 1259 | mp[slot].descr = src; |
4beab9c4 MH |
1260 | } |
1261 | } | |
1262 | ||
1263 | /* | |
1264 | * Implements macros from command mode. c is the buffer to | |
1265 | * get the macro from. | |
1266 | */ | |
1267 | cmdmac(c) | |
1268 | char c; | |
1269 | { | |
1270 | char macbuf[BUFSIZ]; | |
1271 | line *ad, *a1, *a2; | |
1272 | char *oglobp; | |
b8c3ed58 | 1273 | short pk; |
4beab9c4 MH |
1274 | bool oinglobal; |
1275 | ||
1276 | lastmac = c; | |
1277 | oglobp = globp; | |
1278 | oinglobal = inglobal; | |
1279 | pk = peekc; peekc = 0; | |
1280 | if (inglobal < 2) | |
1281 | inglobal = 1; | |
1282 | regbuf(c, macbuf, sizeof(macbuf)); | |
1283 | a1 = addr1; a2 = addr2; | |
1284 | for (ad=a1; ad<=a2; ad++) { | |
1285 | globp = macbuf; | |
1286 | dot = ad; | |
1287 | commands(1,1); | |
1288 | } | |
1289 | globp = oglobp; | |
1290 | inglobal = oinglobal; | |
1291 | peekc = pk; | |
1292 | } |