master xplshn/aruu / cmd / extra / yap / commands.c
  1/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  2
  3#include "commands.h"
  4#include "assert.h"
  5#include "display.h"
  6#include "getcomm.h"
  7#include "getline.h"
  8#include "help.h"
  9#include "in_all.h"
 10#include "keys.h"
 11#include "machine.h"
 12#include "main.h"
 13#include "options.h"
 14#include "output.h"
 15#include "pattern.h"
 16#include "process.h"
 17#include "prompt.h"
 18#include "term.h"
 19
 20static long lastcount; /* Save last count for '.' command */
 21static int  lastcomm;  /* Save last command for '.' command */
 22static int  searchdir; /* Direction of last search */
 23
 24static int  do_nocomm(long cnt);
 25static void do_search(char *str, long cnt, int dir);
 26static int  do_fsearch(long cnt);
 27static int  do_bsearch(long cnt);
 28static int  n_or_rn_search(long cnt, int dir);
 29static int  do_nsearch(long cnt);
 30static int  do_rnsearch(long cnt);
 31static int  shell_command(int esc_ch, long cnt);
 32static int  do_shell(long cnt);
 33static int  do_pipe(long cnt);
 34static int  do_writefile(long cnt);
 35static int  do_absolute(long cnt);
 36static int  do_visit(long cnt);
 37static int  do_error(long cnt);
 38static int  prev_screen(int sz, int really);
 39static int  next_screen(int sz, int really);
 40static int  do_redraw(long cnt);
 41static int  page_size(unsigned cnt);
 42static int  do_forward(long cnt);
 43static int  do_backward(long cnt);
 44static int  do_firstline(long cnt);
 45static int  do_lline(long cnt);
 46static int  do_lf(long cnt);
 47static int  do_upline(long cnt);
 48static int  do_skiplines(long cnt);
 49static int  do_bskiplines(long cnt);
 50static int  do_fscreens(long cnt);
 51static int  do_bscreens(long cnt);
 52static int  scro_size(unsigned cnt);
 53static int  do_f_scroll(long cnt);
 54static int  do_b_scroll(long cnt);
 55static int  do_previousfile(long cnt);
 56static int  do_nextfile(long cnt);
 57static int  do_lcomm(long cnt);
 58static int  do_quit(long cnt);
 59
 60static int
 61do_nocomm(long cnt)
 62{ /* Do nothing */
 63  (void)cnt;
 64  return 0;
 65}
 66
 67int
 68do_chkm(long cnt)
 69{ /* Change key map */
 70  struct keymap *p;
 71  (void)cnt;
 72
 73  if (!(p = othermap)) {
 74    error("No other keymap");
 75    return 0;
 76  }
 77  othermap = currmap;
 78  currmap  = p;
 79  return 0;
 80}
 81
 82/*
 83 * Perform searches
 84 */
 85
 86static void
 87do_search(char *str, long cnt, int dir)
 88{
 89  char *p;
 90  long  lineno;
 91
 92  if (str) {
 93    /*
 94     * We have to get a pattern, which we have to prompt for
 95     * with the string "str".
 96     */
 97    if ((p = readline(str)) == 0) {
 98      /*
 99       * User cancelled command
100       */
101      return;
102    }
103    if ((p = re_comp(p))) {
104      /*
105       * There was an error in the pattern
106       */
107      error(p);
108      return;
109    }
110    searchdir = dir;
111  }
112  if (dir < 0)
113    lineno = scr_info.firstline;
114  else
115    lineno = scr_info.lastline;
116  for (;;) {
117    p = 0;
118    if ((lineno += dir) > 0)
119      p = getline(lineno, 0);
120    if (interrupt)
121      return;
122    if (!p) { /* End of file reached */
123      error("pattern not found");
124      return;
125    }
126    if (re_exec(p) && --cnt <= 0) {
127      /*
128       * We found the pattern, and we found it often enough.
129       * Pity that we still don't know where the match is.
130       * We only know the linenumber. So, we just hope the
131       * following will at least bring it on the screen ...
132       */
133      (void)display(lineno, 0, pagesize, 0);
134      (void)scrollb(2, 0);
135      redraw(0);
136      return;
137    }
138  }
139  /* NOTREACHED */
140}
141
142static int
143do_fsearch(long cnt)
144{ /* Forward search */
145  do_search("/", cnt, 1);
146  return 0;
147}
148
149static int
150do_bsearch(long cnt)
151{ /* Backward search */
152  do_search("?", cnt, -1);
153  return 0;
154}
155
156/*
157 * Repeat last search in direction "dir"
158 */
159
160static int
161n_or_rn_search(long cnt, int dir)
162{
163  char *p;
164
165  if (dir == 1) {
166    p = "/\r";
167  } else if (dir == -1) {
168    p = "?\r";
169  } else {
170    error("No previous pattern");
171    return 0;
172  }
173  if (!stupid)
174    clrbline();
175  putline(p);
176  flush();
177  do_search((char *)0, cnt, dir);
178  return 0;
179}
180
181static int
182do_nsearch(long cnt)
183{ /* Repeat search in same direction */
184  n_or_rn_search(cnt, searchdir);
185  return 0;
186}
187
188static int
189do_rnsearch(long cnt)
190{ /* Repeat search in opposite direction */
191  n_or_rn_search(cnt, -searchdir);
192  return 0;
193}
194
195static int
196shell_command(int esc_ch, long cnt)
197{
198  char       *p;
199  static char buf[2];
200
201  buf[0] = esc_ch;
202  buf[1] = '\0';
203  if ((p = readline(buf)) != 0) {
204    shellescape(p, esc_ch);
205    if (cnt >= 0 && !hardcopy) {
206      p         = startcomm;
207      startcomm = 0;
208      ret_to_continue();
209      putline(TI);
210      if (!p) {
211        /*
212         * Avoid double redraw.
213         * After a "startcomm", a redraw will
214         * take place anyway.
215         */
216        redraw(1);
217      }
218    }
219  }
220  return 0;
221}
222
223static int
224do_shell(long cnt)
225{ /* Execute a shell escape */
226  return shell_command('!', cnt);
227}
228
229static int
230do_pipe(long cnt)
231{ /* Execute a shell escape */
232  return shell_command('|', cnt);
233}
234
235static int
236do_writefile(long cnt)
237{ /* Write input to a file */
238  char *p;
239  int   fd;
240  (void)cnt;
241
242  if ((p = readline("Filename: ")) == 0 || !*p) {
243    /*
244     * No file name given
245     */
246    return 0;
247  }
248  if ((fd = open(p, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
249    if (errno == EEXIST) {
250      error("File exists");
251      return 0;
252    }
253    error("Could not open file");
254    return 0;
255  }
256  wrt_fd(fd);
257  (void)close(fd);
258  return 0;
259}
260
261void
262wrt_fd(int fd)
263{
264  long  l = 1;
265  char *p = getline(l, 0), *pbuf;
266  char  buf[1024];
267
268  while (p) {
269    pbuf = buf;
270    while (p && pbuf < &buf[1024]) {
271      if (!*p) {
272        *pbuf++ = '\n';
273        p       = getline(++l, 0);
274      } else
275        *pbuf++ = *p++;
276    }
277    if (write(fd, buf, pbuf - buf) < 0) {
278      error("Write failed");
279      break;
280    }
281  }
282}
283
284static int
285do_absolute(long cnt)
286{ /* Go to linenumber "cnt" */
287
288  if (!getline(cnt, 0)) { /* Not there or interrupt */
289    if (!interrupt) {
290      /*
291       * User did'nt give an interrupt, so the line number
292       * was too high. Go to the last line.
293       */
294      do_lline(cnt);
295    }
296    return 0;
297  }
298  (void)display(cnt, 0, pagesize, 1);
299  return 0;
300}
301
302static int
303do_visit(long cnt)
304{ /* Visit a file */
305  char       *p;
306  static char fn[128]; /* Keep file name */
307  (void)cnt;
308
309  if ((p = readline("Filename: ")) == 0) {
310    return 0;
311  }
312  if (*p) {
313    (void)strcpy(fn, p);
314    visitfile(fn);
315  } else {
316    /*
317     * User typed a return. Visit the current file
318     */
319    if (!(p = filenames[filecount])) {
320      error("No current file");
321      return 0;
322    }
323    visitfile(p);
324  }
325  (void)display(1L, 0, pagesize, 1);
326  return 0;
327}
328
329static int
330do_error(long cnt)
331{ /* Called when user types wrong key sequence */
332  (void)cnt;
333  error(currmap->k_help);
334  return 0;
335}
336
337/*
338 * Interface routine for displaying previous screen,
339 * depending on cflag.
340 */
341
342static int
343prev_screen(int sz, int really)
344{
345  int retval;
346
347  retval = scrollb(sz - 1, really && cflag);
348  if (really && !cflag) {
349    /*
350     * The previous call did not display anything, but at least we
351     * know where to start
352     */
353    return display(scr_info.firstline, scr_info.nf, sz, 1);
354  }
355  return retval;
356}
357
358/*
359 * Interface routine for displaying the next screen,
360 * dependent on cflag.
361 */
362
363static int
364next_screen(int sz, int really)
365{
366  int              t;
367  struct scr_info *p = &scr_info;
368
369  if (cflag) {
370    return scrollf(sz - 1, really);
371  }
372  t = p->tail->cnt - 1;
373  if (p->lastline == p->firstline) {
374    t += p->nf;
375  }
376  return display(p->lastline, t, sz, really);
377}
378
379static int
380do_redraw(long cnt)
381{
382  (void)cnt;
383  redraw(1);
384  return 0;
385}
386
387static int
388page_size(unsigned cnt)
389{
390  if (cnt) {
391    if (cnt > (unsigned)maxpagesize)
392      return maxpagesize;
393    if (cnt < MINPAGESIZE)
394      return MINPAGESIZE;
395    return (int)cnt;
396  }
397  return pagesize;
398}
399
400static int
401do_forward(long cnt)
402{ /* Display next page */
403  int i;
404
405  i = page_size((unsigned)cnt);
406  if (status & EOFILE) {
407    /*
408     * May seem strange, but actually a visit to the next file
409     * has already been done here
410     */
411    (void)display(1L, 0, i, 1);
412    return 0;
413  }
414  (void)next_screen(i, 1);
415  return 0;
416}
417
418static int
419do_backward(long cnt)
420{
421  int i, temp;
422
423  i = page_size((unsigned)cnt);
424  if (!(status & START)) {
425    (void)prev_screen(i, 1);
426    return 0;
427  }
428  if (stdf < 0) {
429    (void)display(1L, 0, i, 1);
430    return 0;
431  }
432  /*
433   * The next part is a bit clumsy.
434   * We want to display the last page of the previous file (for which
435   * a visit has already been done), but the pagesize may temporarily
436   * be different because the command had a count
437   */
438  temp     = pagesize;
439  pagesize = i;
440  do_lline(cnt);
441  pagesize = temp;
442  return 0;
443}
444
445static int
446do_firstline(long cnt)
447{ /* Go to start of input */
448  (void)cnt;
449  do_absolute(1L);
450  return 0;
451}
452
453static int
454do_lline(long cnt)
455{ /* Go to end of input */
456  int i = 0;
457  int j = pagesize - 1;
458
459  if ((cnt = to_lastline()) < 0) {
460    /*
461     * Interrupted by the user
462     */
463    return 0;
464  }
465  /*
466   * Display the page such that only the last line of the page is
467   * a "~", independant of the pagesize
468   */
469  while (!(display(cnt, i, j, 0) & EOFILE)) {
470    /*
471     * The last line could of course be very long ...
472     */
473    i += j;
474  }
475  (void)scrollb(j - scr_info.tail->cnt, 0);
476  redraw(0);
477  return 0;
478}
479
480static int
481do_lf(long cnt)
482{ /* Display next line, or go to line */
483
484  if (cnt) { /* Go to line */
485    do_absolute(cnt);
486    return 0;
487  }
488  (void)scrollf(1, 1);
489  return 0;
490}
491
492static int
493do_upline(long cnt)
494{ /* Display previous line, or go to line */
495
496  if (cnt) { /* Go to line */
497    do_absolute(cnt);
498    return 0;
499  }
500  (void)scrollb(1, 1);
501  return 0;
502}
503
504static int
505do_skiplines(long cnt)
506{ /* Skip lines forwards */
507
508  /* Should be interruptable ... */
509  (void)scrollf((int)(cnt + maxpagesize - 1), 0);
510  redraw(0);
511  return 0;
512}
513
514static int
515do_bskiplines(long cnt)
516{ /* Skip lines backwards */
517
518  /* Should be interruptable ... */
519  (void)scrollb((int)(cnt + pagesize - 1), 0);
520  redraw(0);
521  return 0;
522}
523
524static int
525do_fscreens(long cnt)
526{ /* Skip screens forwards */
527
528  do {
529    if ((next_screen(pagesize, 0) & EOFILE) || interrupt)
530      break;
531  } while (--cnt >= 0);
532  redraw(0);
533  return 0;
534}
535
536static int
537do_bscreens(long cnt)
538{ /* Skip screens backwards */
539
540  do {
541    if ((prev_screen(pagesize, 0) & START) || interrupt)
542      break;
543  } while (--cnt >= 0);
544  redraw(0);
545  return 0;
546}
547
548static int
549scro_size(unsigned cnt)
550{
551  if (cnt >= (unsigned)maxpagesize)
552    return maxpagesize;
553  if (cnt)
554    return (int)cnt;
555  return scrollsize;
556}
557
558static int
559do_f_scroll(long cnt)
560{ /* Scroll forwards */
561
562  (void)scrollf(scro_size((unsigned)cnt), 1);
563  return 0;
564}
565
566static int
567do_b_scroll(long cnt)
568{ /* Scroll backwards */
569
570  (void)scrollb(scro_size((unsigned)cnt), 1);
571  return 0;
572}
573
574static int
575do_previousfile(long cnt)
576{ /* Visit previous file */
577
578  if (nextfile(-(int)cnt)) {
579    error("No (Nth) previous file");
580    return 0;
581  }
582  redraw(0);
583  return 0;
584}
585
586static int
587do_nextfile(long cnt)
588{ /* Visit next file */
589
590  if (nextfile((int)cnt)) {
591    error("No (Nth) next file");
592    return 0;
593  }
594  redraw(0);
595  return 0;
596}
597
598static int
599do_quit(long cnt)
600{
601  (void)cnt;
602  return quit();
603}
604
605/*
606 * The next array is initialized, sorted on the first element of the structs,
607 * so that we can perform binary search
608 */
609struct commands commands[] = {
610    {"", 0, do_error, ""},
611    {"", 0, do_nocomm, ""},
612    {"bf", STICKY | NEEDS_COUNT, do_previousfile, "Visit previous file"},
613    {"bl", NEEDS_SCREEN | STICKY, do_upline, "Scroll one line up, or go to line"},
614    {"bot", STICKY, do_lline, "Go to last line of the input"},
615    {"bp", BACK | NEEDS_SCREEN | TOPREVFILE | STICKY, do_backward, "display previous page"},
616    {"bps",
617     SCREENSIZE_ADAPT | BACK | NEEDS_SCREEN | TOPREVFILE | STICKY,
618     do_backward,
619     "Display previous page, set pagesize"},
620    {"bs", BACK | NEEDS_SCREEN | STICKY, do_b_scroll, "Scroll backwards"},
621    {"bse", 0, do_bsearch, "Search backwards for pattern"},
622    {"bsl", BACK | NEEDS_SCREEN | STICKY | NEEDS_COUNT, do_bskiplines, "Skip lines backwards"},
623    {"bsp", BACK | NEEDS_SCREEN | STICKY | NEEDS_COUNT, do_bscreens, "Skip screens backwards"},
624    {"bss",
625     SCROLLSIZE_ADAPT | BACK | NEEDS_SCREEN | STICKY,
626     do_b_scroll,
627     "Scroll backwards, set scrollsize"},
628    {"chm", 0, do_chkm, "Switch to other keymap"},
629    {"exg", STICKY, exgmark, "Exchange current page with mark"},
630    {"ff", STICKY | NEEDS_COUNT, do_nextfile, "Visit next file"},
631    {"fl", NEEDS_SCREEN | STICKY, do_lf, "Scroll one line down, or go to line"},
632    {"fp", TONEXTFILE | AHEAD | STICKY, do_forward, "Display next page"},
633    {"fps",
634     SCREENSIZE_ADAPT | TONEXTFILE | AHEAD | STICKY,
635     do_forward,
636     "Display next page, set pagesize"},
637    {"fs", AHEAD | NEEDS_SCREEN | STICKY, do_f_scroll, "Scroll forwards"},
638    {"fse", 0, do_fsearch, "Search forwards for pattern"},
639    {"fsl", AHEAD | NEEDS_SCREEN | STICKY | NEEDS_COUNT, do_skiplines, "Skip lines forwards"},
640    {"fsp", AHEAD | NEEDS_SCREEN | STICKY | NEEDS_COUNT, do_fscreens, "Skip screens forwards"},
641    {"fss",
642     SCROLLSIZE_ADAPT | AHEAD | NEEDS_SCREEN | STICKY,
643     do_f_scroll,
644     "Scroll forwards, set scrollsize"},
645    {"hlp", 0, do_help, "Give description of all commands"},
646    {"mar", 0, setmark, "Set a mark on the current page"},
647    {"nse", STICKY, do_nsearch, "Repeat the last search"},
648    {"nsr", STICKY, do_rnsearch, "Repeat last search in other direction"},
649    {"pip", ESC, do_pipe, "pipe input into shell command"},
650    {"qui", 0, do_quit, "Exit from yap"},
651    {"red", 0, do_redraw, "Redraw screen"},
652    {"rep", 0, do_lcomm, "Repeat last command"},
653    {"shl", ESC, do_shell, "Execute a shell escape"},
654    {"tom", 0, tomark, "Go to mark"},
655    {"top", STICKY, do_firstline, "Go to the first line of the input"},
656    {"vis", 0, do_visit, "Visit a file"},
657    {"wrf", 0, do_writefile, "Write input to a file"},
658};
659
660/*
661 * Lookup string "s" in the commands array, and return index.
662 * return 0 if not found.
663 */
664
665int
666lookup(char *s)
667{
668  struct commands *l, *u, *m;
669
670  l = &commands[2];
671  u = &commands[sizeof(commands) / sizeof(*u) - 1];
672  do {
673    /*
674     * Perform binary search
675     */
676    m = l + (u - l) / 2;
677    if (strcmp(s, m->c_cmd) > 0)
678      l = m + 1;
679    else
680      u = m;
681  } while (l < u);
682  if (!strcmp(s, u->c_cmd))
683    return u - commands;
684  return 0;
685}
686
687/*ARGSUSED*/
688static int
689do_lcomm(long cnt)
690{ /* Repeat last command */
691  (void)cnt;
692
693  if (!lastcomm) {
694    error("No previous command");
695    return 0;
696  }
697  do_comm(lastcomm, lastcount);
698  return 0;
699}
700
701/*
702 * Execute a command, with optional count "count".
703 */
704
705void
706do_comm(int comm, long count)
707{
708  struct commands *pcomm;
709  int              temp;
710  int              flags;
711
712  pcomm = &commands[comm];
713  flags = pcomm->c_flags;
714
715  /*
716   * Check the command.
717   * If the last line of the file is displayed and the command goes
718   * forwards and does'nt have the ability to go to the next file, it
719   * is an error.
720   * If the first line of the file is displayed and the command goes
721   * backwards and does'nt have the ability to go to the previous file,
722   * it is an error.
723   * Also check wether we need the next or previous file. If so, get it.
724   */
725  if ((status & EOFILE) && (flags & AHEAD)) {
726    if (qflag || !(flags & TONEXTFILE))
727      return;
728    if (nextfile(1))
729      quit();
730  }
731  if ((status & START) && (flags & BACK)) {
732    if (qflag || !(flags & TOPREVFILE))
733      return;
734    if (nextfile(-1))
735      quit();
736  }
737  /*
738   * Does the command stick around for LASTCOMM?
739   */
740  if (flags & STICKY) {
741    lastcomm  = comm;
742    lastcount = count;
743  }
744  if (!count) {
745    if (flags & NEEDS_COUNT)
746      count = 1;
747  } else {
748    /*
749     * Does the command adapt the screensize?
750     */
751    if (flags & SCREENSIZE_ADAPT) {
752      temp = maxpagesize;
753      if ((unsigned)count < (unsigned)temp) {
754        temp = (int)count;
755      }
756      if (temp < MINPAGESIZE) {
757        temp = MINPAGESIZE;
758      }
759      count    = 0;
760      pagesize = temp;
761    }
762    /*
763     * Does the command adapt the scrollsize?
764     */
765    if (flags & SCROLLSIZE_ADAPT) {
766      temp = maxpagesize - 1;
767      if ((unsigned)count < (unsigned)temp) {
768        temp = (int)count;
769      }
770      scrollsize = temp;
771      count      = 0;
772    }
773  }
774  /*
775   * Now execute the command.
776   */
777  (*(pcomm->c_func))(count);
778}