home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume3 / pcmail / part01 / pager.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-02-03  |  13.8 KB  |  545 lines

  1. /*++
  2. /* NAME
  3. /*      pager 3
  4. /* SUMMARY
  5. /*      pager for text files
  6. /* PROJECT
  7. /*      pc-mail
  8. /* PACKAGE
  9. /*      mailsh
  10. /* SYNOPSIS
  11. /*      #include "pager.h"
  12. /*
  13. /*      File *open_pager()
  14. /*
  15. /*      void close_pager(p)
  16. /*      File *p;
  17. /*
  18. /*      void set_pager(p)
  19. /*      File *p;
  20. /*
  21. /*      void app_pager(p,s)
  22. /*      File *p;
  23. /*      char *s;
  24. /*
  25. /*      void del_pager(p)
  26. /*      File *p;
  27. /*
  28. /*      void mesg_pager(p,m)
  29. /*      File *p;
  30. /*      char *m[];
  31. /*
  32. /*      int scan_pager(p,fmt[,args])
  33. /*      File *p;
  34. /*
  35. /*      void sort_pager(p,dir)
  36. /*      File *p;
  37. /*
  38. /*      int cp_pager(path)
  39. /*      char *path;
  40. /*
  41. /*      int pr_pager()
  42. /*
  43. /*      int rd_pager(p,path)
  44. /*      File *p;
  45. /*      char *path;
  46. /*
  47. /*      char *gets_pager();
  48. /*
  49. /*      void puts_pager(s);
  50. /*      char *s;
  51. /*
  52. /*      int ds_pager()
  53. /*
  54. /*      int pr_pager()
  55. /*
  56. /*      int up_pager()
  57. /*
  58. /*      int dn_pager()
  59. /*
  60. /*      int pu_pager()
  61. /*
  62. /*      int pd_pager()
  63. /* DESCRIPTION
  64. /*      The pager provides acces to a pager file which is displayed
  65. /*      on the screen in the middle window. Some functions operate
  66. /*    on what is called the "current" pager file. All functions 
  67. /*    have access to the contents of the middle screen window only.
  68. /*
  69. /*      open_pager() creates a new (empty) pager file. The return value
  70. /*      should be used in subsequent accesses to the file. Sets the
  71. /*      current file.
  72. /*
  73. /*      close_pager() releases storage for a pager file. Sets the
  74. /*      current file to none if that is the one being deleted.
  75. /*
  76. /*      app_pager() appends a new line of text to the end of a pager file.
  77. /*      Sets the current file.
  78. /*
  79. /*      del_pager() deletes the line at the current cursor position. Sets the
  80. /*      current file.
  81. /*
  82. /*      mesg_pager() invokes app_pager() to copy a null-terminated array of 
  83. /*    strings to a pager file. Since it invokes app_pager(), the current
  84. /*    pager file is set as well. Pager files filled by mesg-pager()
  85. /*    will not be displayed with an '-- end of display --' line at their end.
  86. /*
  87. /*      ins_pager() inserts a line at the current cursor position. Sets the
  88. /*      current file.
  89. /*
  90. /*      scan_pager() takes similar arguments as scanf(3), reads from the 
  91. /*      line at the current cursor position and returns the number of 
  92. /*      successfull conversions done. Does not set the current file.
  93. /*
  94. /*      sort_pager() sorts a file alphabetically. Sets the current file.
  95. /*      The dir argument selects the direction of sort (FORW_SORT, BACK_SORT).
  96. /*
  97. /*      set_pager() sets the current file.
  98. /*
  99. /*      gets_pager() returns a pointer to the current line in the
  100. /*      current file, or a null pointer is there is none.
  101. /*
  102. /*      puts_pager() replaces the current line in the current file.
  103. /*
  104. /*      cp_pager() copies the contents of current pager file
  105. /*      to a normal (external) file. It returns a nonzero status if
  106. /*      an error occurred (bad path, write error,...).
  107. /*
  108. /*      pr_pager() copies the current pager file to the printer.
  109. /*
  110. /*      rd_pager() appends a permanent file to the current pager file.
  111. /*      It returns a nonzero status if an error occurred. Sets the current file.
  112. /*    rd_pager() reads through the ascf(3) ascii filter, so that it is 
  113. /*    suitable for viewing word-processor files.
  114. /*
  115. /*      up_pager() moves the cursor one line up, if there is one. The
  116. /*    screen is scrolled when the cursor was at the top of the screen.
  117. /*
  118. /*      dn_pager moves the cursor one line down, if there is one. The
  119. /*    screen is scrolled when the cursor was at the bottom of the screen.
  120. /*
  121. /*      pu_pager() displays a page of text that precedes the one on the
  122. /*      screen, or as much as there is.
  123. /*
  124. /*      pd_pager() displays the next page of text, or as much as there is.
  125. /* FUNCTIONS AND MACROS
  126. /*      printcl(), printat(), beep(), propen(), prclose(),ascopen(), 
  127. /*    ascclose(), ascget()
  128. /* SEE ALSO
  129. /*      path(3), window(3), window(5), asc(3)
  130. /* DIAGNOSTICS
  131. /*      The buzzer makes noise when attempt is made to move the
  132. /*      cursor beyond the beginning or end of the pager file.
  133. /*      The program aborts with an error message if references are made
  134. /*      to the "current file" or "current line" if there is none.
  135. /* BUGS
  136. /*      It looks a lot like an editor, but it isn't.
  137. /*
  138. /*    No optimization. It just overwrites the whole middle window.
  139. /* AUTHOR(S)
  140. /*      W.Z. Venema
  141. /*      Eindhoven University of Technology
  142. /*      Department of Mathematics and Computer Science
  143. /*      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  144. /* CREATION DATE
  145. /*      Fri Apr  3 22:06:00 GMT+1:00 1987
  146. /* LAST MODIFICATION
  147. /*    Mon Apr  4 23:46:06 MET 1988
  148. /* VERSION/RELEASE
  149. /*    1.3
  150. /*--*/
  151.  
  152. #include "defs.h"
  153. #include "window.h"
  154. #include "pager.h"
  155. #include "path.h"
  156. #include "ascf.h"
  157.  
  158. hidden File *curfile = NULL;            /* head of the current file */
  159.  
  160. /* open_pager - create pager file and set current file */
  161.  
  162. public File *open_pager()
  163. {
  164.     register File *p = curfile = (File *) myalloc(sizeof(File));
  165.  
  166.     p->top = p->curr = p->head = p->last = NULL;
  167.     p->opts = 0;
  168.     return(p);
  169. }
  170.  
  171. /* close_pager - release memory in a pager file */
  172.  
  173. public void close_pager(p)
  174. register File *p;
  175. {
  176.     register Line *q;
  177.  
  178.     if (p) {
  179.     for (q = p->head; q; q = q->next)    /* release lines */
  180.         free((char *)q);
  181.     if (curfile == p)            /* unset current file */
  182.         curfile = 0;
  183.     free((char *)p);            /* release header */
  184.     }
  185. }
  186.  
  187. /* app_pager - append line at end of file and set current file */
  188.  
  189. public app_pager(p,s)
  190. register File *p;
  191. char *s;
  192. {
  193.     register Line *l = (Line *) myalloc(sizeof(Line)+strlen(s));
  194.  
  195.     if (p->head == 0) {                         /* first line in the file */
  196.     p->head = p->top = p->curr = l;
  197.     } else {                                    /* real append */
  198.     p->last->next = l;
  199.     }
  200.     l->next = NULL;                             /* since it is last */
  201.     l->prev = p->last;                          /* since it is last */
  202.     p->last = l;                                /* since it is last */
  203.  
  204.     strcpy(l->line,s);                          /* copy the line */
  205.  
  206.     curfile = p;                                /* set current file */
  207. }
  208.  
  209. /* del_pager - delete line at cursor and set current file (untested!) */
  210.  
  211. public void del_pager(p)
  212. register File *p;
  213. {
  214.     register Line *l = p->curr;
  215.  
  216.     if (l) {
  217.     if (l->prev)
  218.         l->prev->next = l->next;
  219.     if (l->next)
  220.         l->next->prev = l->prev;
  221.     if (l == p->head)
  222.         p->curr = p->head = l->next;
  223.     if (l == p->top)
  224.         p->curr = p->top  = l->next ? l->next : l->prev;
  225.     if (l == p->last)
  226.         p->curr = p->last = l->prev;
  227.     if (l == p->curr)
  228.         p->curr = l->next;
  229.     free((char *) l);
  230.     }
  231.     curfile = p;
  232. }
  233.  
  234. /* scan_pager - read from line at cursor and set current file */
  235.  
  236. /* VARARGS2 */
  237.  
  238. public int scan_pager(p,fmt,a1,a2,a3,a4)
  239. File *p;
  240. char *fmt;
  241. long a1,a2,a3,a4;
  242. {
  243.     return(p && p->curr ? sscanf(p->curr->line,fmt,a1,a2,a3,a4) : 0);
  244. }
  245.  
  246. /* set_pager - set the current file; use with care */
  247.  
  248. public void set_pager(p)
  249. File *p;
  250. {
  251.     curfile = p;
  252. }
  253.  
  254. /*
  255. * The following functions provide an easy interface to the keyboard
  256. * interpreter routines. The keyboard driver just associates a key stroke
  257. * with a function call, does not care what a function does and has
  258. * almost no facility for passing function arguments.
  259. * Although the keyboard interpreter manipulates cursor and page coordinates 
  260. * when the some keys are hit, it never knows which keys affect what
  261. * the user sees on the screen.
  262. * That explains why the following functions rely on the above ones
  263. * for setting the "current file".
  264. * It may also explain why the above routines do not immediately update
  265. * the terminal screen, whereas the routines below do.
  266. */
  267.  
  268. /* ds_pager - display a page of the current pager file */
  269.  
  270. public int ds_pager()
  271. {
  272.     static char endline[] = "-- end of display --";
  273.  
  274.     if (curfile && curfile->curr) {
  275.     register Line *p;
  276.     register int k;
  277.  
  278.     for (p = curfile->top,k = 0; p && k < wsize[MID]; p = p->next)
  279.         k += p->llen = printcl(MID,p->lineno = k,p->line);
  280.     if (k < wsize[MID])
  281.         printcl(MID,k++,(curfile->opts & PG_NOEND) ? "" : endline);
  282.     while (k < wsize[MID])
  283.         printcl(MID,k++,"");
  284.     printat(MID,curfile->curr->lineno,"");
  285.     } else {
  286.     register int k;
  287.  
  288.     printcl(MID,0,(curfile->opts & PG_NOEND) ? "" : endline);
  289.     for (k = 1; k < wsize[MID]; k++)
  290.         printcl(MID,k,"");
  291.     printat(MID,0,"");
  292.     }
  293.     return(0);  /* screen up-to-date */
  294. }
  295.  
  296. /* up_pager - up-arrow key hit. check cursor position */
  297.  
  298. public int up_pager()
  299. {
  300.     register Line *p = curfile ? curfile->curr : 0;
  301.  
  302.     if (p == 0 || p->prev == 0) {
  303.     beep();
  304.     } else {
  305.     if (p->lineno == 0)
  306.         pu_pager();
  307.     printat(MID,(curfile->curr = p->prev)->lineno,"");
  308.     }
  309.     return(0);
  310. }
  311.  
  312. /* dn_pager - down-arrow key hit. check cursor position */
  313.  
  314. public int dn_pager()
  315. {
  316.     register Line *p = curfile ? curfile->curr : 0;
  317.  
  318.     if (p == 0 || p->next == 0) {
  319.     beep();
  320.     } else {
  321.     if (p->lineno+p->llen >= wsize[MID])
  322.         pd_pager();
  323.     printat(MID,(curfile->curr = p->next)->lineno,"");
  324.     }
  325.     return(0);
  326. }
  327.  
  328. /* pu_pager - display preceding page of info */
  329.  
  330. public int pu_pager()
  331. {
  332.     register Line *p;
  333.     register int k;
  334.  
  335.     if (curfile && (p = curfile->top) && curfile->top->prev) {
  336.     for (k = 0; k < wsize[MID] && p; k += p->llen,p = p->prev)
  337.         curfile->top = p;
  338.     curfile->curr = curfile->top;
  339.     ds_pager();
  340.     } else {
  341.     beep();
  342.     }
  343.     return(0);
  344. }
  345.  
  346. /* pd_pager - display next page of info */
  347.  
  348. public int pd_pager()
  349. {
  350.     register Line *p = curfile ? curfile->top : 0;
  351.     register int k;
  352.     register Line *dummy;
  353.  
  354.     for (k = 0; k < wsize[MID] && p; k += p->llen,p = p->next)
  355.     dummy = p;
  356.     if (p) {
  357.     curfile->curr = curfile->top = dummy;
  358.     ds_pager();
  359.     } else {
  360.     beep();
  361.     }
  362.     return(0);
  363. }
  364.  
  365. /*
  366. * The following functions copy permanent files to pager file
  367. * and vice-versa. There is a limited error detection facility
  368. * in the form of nonzero return values.
  369. */
  370.  
  371. /* cp_pager - copy current pager file to permanent file */
  372.  
  373. public int cp_pager(path)
  374. char *path;
  375. {
  376.     register FILE *fp;
  377.  
  378.     if (curfile && (fp = fopen(path,"w"))) {
  379.     register Line *pp;
  380.     int err;
  381.     for (pp = curfile->head; pp; pp = pp->next)
  382.         fputs(pp->line,fp),putc('\n',fp);
  383.     err = ferror(fp);
  384.     fclose(fp);
  385.     return(err);
  386.     } else {
  387.     return(-1);
  388.     }
  389. }
  390.  
  391. /* pr_pager - print pager file on default printer */
  392.  
  393. public int pr_pager()
  394. {
  395.     register FILE *fp;
  396.  
  397.     if (curfile && (fp = propen())) {
  398.     register Line *pp;
  399.     int err;
  400.     for (pp = curfile->head; pp; pp = pp->next)
  401.         fputs(pp->line,fp),putc('\n',fp);
  402.     err = ferror(fp);
  403.     prclose(fp);
  404.     return(err);
  405.     } else {
  406.     return(-1);
  407.     }
  408. }
  409.  
  410. /* rd_pager - copy ordinary file via filter to pager file */
  411.  
  412. public int rd_pager(p,path)
  413. File *p;
  414. char *path;
  415. {
  416.     register FILE *fp;
  417.  
  418.     if (p && (fp = ascopen(path,"r"))) {        /* init the filter */
  419.     char buf[BUFSIZ];
  420.     char *cp = buf;
  421.     register int c;
  422.     int err;
  423.  
  424.     while ((c = ascget(fp)) != EOF) {        /* copy to pager file */
  425.         if (c == '\n' || cp == buf+BUFSIZ-1) {
  426.         *cp = 0;
  427.         app_pager(p,cp = buf);                  /* line by line */
  428.         } else {
  429.         *cp++ = c; 
  430.         }
  431.     }
  432.     if (cp > buf) {                    /* anything left? */
  433.         *cp = '\0';
  434.         app_pager(p,buf);
  435.     }
  436.     err = ferror(fp);                /* check for errors */
  437.     ascclose(fp);
  438.     return(err);
  439.     } else {
  440.     return(-1);
  441.     }
  442. }
  443.  
  444. /* fwdcmp, revcmp - compare lexical order of lines */
  445.  
  446. hidden int fwdcmp(l1,l2)
  447. Line **l1,**l2;
  448. {
  449.     return(strcmp((*l1)->line,(*l2)->line));
  450. }
  451.  
  452. hidden int revcmp(l1,l2)
  453. Line **l1,**l2;
  454. {
  455.     return(strcmp((*l2)->line,(*l1)->line));
  456. }
  457.  
  458. /* sort_pager - sort a pager file, a boring thing */
  459.  
  460. public void sort_pager(pp,dir)
  461. File *pp;
  462. int dir;
  463. {
  464.     register Line *l;
  465.     register int i;
  466.     int lines;
  467.     Line **lvec;
  468.  
  469.     for (i = 0,l = pp->head; l; l = l->next)            /* count nbr of lines */
  470.     i++;
  471.  
  472.     if (i <= 1)                                         /* no work */
  473.     return;
  474.  
  475.     lvec = (Line **) myalloc((lines = i)*sizeof(*lvec));/* allocate vector */
  476.  
  477.     for (i = 0,l = pp->head; l; l = l->next)            /* fill vector */
  478.     lvec[i++] = l;
  479.  
  480.     qsort((char *) lvec,lines,sizeof(*lvec),dir == FORW_SORT ? fwdcmp : revcmp);
  481.  
  482.     for (i = 0; i < lines-1; i++)                       /* fix forward links */
  483.     lvec[i]->next = lvec[i+1];
  484.     lvec[i]->next = NULL;
  485.  
  486.     lvec[0]->prev = NULL;                               /* fix backward links */
  487.     for (i = 1; i < lines; i++)
  488.     lvec[i]->prev = lvec[i-1];
  489.  
  490.     pp->head = pp->top = pp->curr = lvec[0];            /* fix file header */
  491.     pp->last = lvec[lines-1];
  492.  
  493.     free((char *) lvec);                                /* release vector */
  494.  
  495.     curfile = pp;                                       /* set current file */
  496. }
  497.  
  498. /* gets_pager - return current line in current file */
  499.  
  500. public char *gets_pager()
  501. {
  502.     return(curfile && curfile->curr ? curfile->curr->line : 0);
  503. }
  504.  
  505. /* puts_pager - replace line (cleanup this mess) */
  506.  
  507. public void puts_pager(s)
  508. char *s;
  509. {
  510.     if (curfile == 0 || curfile->curr == 0) {   /* no-no if there is no line */
  511.     fatal("puts_pager: no current file");
  512.     } else {
  513.     register Line *old = curfile->curr;     /* get current line */
  514.     register Line *new = (Line *) myalloc(sizeof(Line)+strlen(s));
  515.     new->prev = old->prev;                  /* fill it in */
  516.     new->next = old->next;
  517.     new->lineno = old->lineno;
  518.     strcpy(new->line,s);
  519.     if (new->next)                          /* check next line */
  520.         new->next->prev = new;
  521.     if (new->prev)                          /* check previous line */
  522.         new->prev->next = new;
  523.     if (old == curfile->head)               /* check file head */
  524.         curfile->head = new;
  525.     if (old == curfile->top)                /* check file display */
  526.         curfile->top = new;
  527.     if (old == curfile->last)               /* check file tail */
  528.         curfile->last = new;
  529.     free((char *) curfile->curr);           /* release old line */
  530.     curfile->curr = new;                    /* set current line */
  531.     }
  532. }
  533.  
  534. /* mesg_pager - copy null-terminated array of strings to pager file */
  535.  
  536. public void mesg_pager(pp,msg)
  537. register File *pp;
  538. register char **msg;
  539. {
  540.     pp->opts |= PG_NOEND;            /* suppress end marker */
  541.  
  542.     while (*msg)
  543.     app_pager(pp,*msg++);
  544. }
  545.