home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / memacs400_src.lzh / MEMACS400 / SRC / undo.c < prev    next >
Text File  |  1996-04-25  |  11KB  |  516 lines

  1. /*    UNDO.C:        Undo commands and functionality for
  2.  *            MicroEMACS
  3.  *            (C)Copyright 1995 by Daniel Lawrence
  4.  *
  5.  *    The functions in this file record and allow the playback
  6.  *    of basic editing changes. For each buffer, a stack of these
  7.  *    changes is maintained. The beginning of each command which
  8.  *    can change the buffer is flaged with a command entry. The
  9.  *    undo command then can back out of these changes one command
  10.  *    at a time. The UNDO stack is flushed by any command that
  11.  *    writes the buffer to disk, or clears the buffer.
  12.  */
  13.  
  14. #include    <stdio.h>
  15. #include    "estruct.h"
  16. #include    "eproto.h"
  17. #include    "edef.h"
  18. #include    "elang.h"
  19.  
  20. /* insert an editing operation at the top of the current buffer's
  21.    undo stack. */
  22.  
  23. #if    PROTO
  24. VOID undo_insert(OPTYPE op_type, long count, OBJECT op_erand)
  25. #else
  26. VOID undo_insert(op_type, count, op_erand)
  27.  
  28. OPTYPE op_type;        /* type of operation being recorded */
  29. long count;        /* operand count */
  30. OBJECT op_erand;    /* the operand of the operation */
  31. #endif
  32. {
  33.     int undo_size;    /* size of the undo object */
  34.     UNDO_OBJ *up;    /* ptr to a newly allocated undo object */
  35.  
  36.     /* don't bother if we are not keeping track! */
  37.     if (undoflag == FALSE)
  38.         return;
  39.  
  40.     /* or don't do it if we are admist undoing something else */
  41.     if (undoing == TRUE)
  42.         return;
  43.  
  44.     /* if it is a command object, and the last one pushed
  45.        was one as well, don't bother */
  46.     if ((curbp->undo_head != (UNDO_OBJ *)NULL) &&
  47.         (op_type == OP_CMND) &&
  48.         (curbp->undo_head->type == OP_CMND))
  49.         return;
  50.  
  51.     /* if it is a insert char, and the last one pushed
  52.        was one as well, just increment its count */
  53.     if ((curbp->undo_head != (UNDO_OBJ *)NULL) &&
  54.         (op_type == OP_INSC) &&
  55.         (curbp->undo_head->type == OP_INSC)) {
  56.             curbp->undo_head->count++;
  57.         return;
  58.     }
  59.  
  60.     /* get the correct size for this entry */
  61.     undo_size = sizeof(UNDO_OBJ);
  62.     if (op_type == OP_DSTR)
  63.         undo_size += (int)count;
  64.  
  65.     /* and allocate the memory */
  66.     up = (UNDO_OBJ *)malloc(undo_size);
  67.     if (up == (UNDO_OBJ *)NULL)
  68.         return;
  69.  
  70.     /* update the buffer undo count */
  71.     curwp->w_bufp->undo_count++;
  72.  
  73.     /* record the buffer position and undo object type */
  74.     up->line_num = getlinenum(curbp, curwp->w_dotp);
  75.     up->offset = curwp->w_doto;
  76.     up->type = op_type;
  77.     up->count = count;
  78.  
  79.     /* save the actual object... */
  80.     switch (op_type) {
  81.  
  82.         case OP_CMND:
  83.         case OP_ISTR:
  84.         case OP_CPOS:
  85.             break;
  86.  
  87.         case OP_DSTR:
  88.             strncpy(up->undo_obj.obj_string,
  89.                 op_erand.obj_sptr, (int)count);
  90.             up->undo_obj.obj_string[(int)count] = 0;
  91.             break;
  92.  
  93.         case OP_REPC:
  94.         case OP_DELC:
  95.         case OP_INSC:
  96.             up->undo_obj.obj_char = op_erand.obj_char;
  97.             break;
  98.     }
  99.  
  100.     /* and add it to the head of the current buffer's undo list */
  101.     up->next = curbp->undo_head;
  102.     curbp->undo_head = up;
  103.     return;
  104. }
  105.  
  106. /* reverse the editing operation at the top of the current buffer's
  107.    undo stack. */
  108.  
  109. #if    PROTO
  110. int undo_op(void)
  111. #else
  112. int undo_op()
  113. #endif
  114. {
  115.     OPTYPE op_type;        /* type of operation being recorded */
  116.     UNDO_OBJ *up;        /* ptr to the undo object */
  117.     int status;        /* return status from undone operation */
  118.     long count;        /* repeat count for some operations */
  119.  
  120.     /* don't bother if we are not keeping track! */
  121.     if (undoflag == FALSE)
  122.         return(FALSE);
  123.  
  124.     /* make sure we flag we are doing an undo.... [avoid recursion!] */
  125.     undoing = TRUE;
  126.  
  127.     /* grab the operation to undo */
  128.     up = curwp->w_bufp->undo_head;
  129.  
  130.     /* restore the buffer position */
  131.     gotoline(TRUE, up->line_num);
  132.     curwp->w_doto = up->offset;
  133.     op_type = up->type;
  134.  
  135.     /* undo the actual operation */
  136.     status = FALSE;
  137.     switch (op_type) {
  138.  
  139.         case OP_CMND:
  140.         case OP_CPOS:
  141.             status = TRUE;
  142.             break;
  143.  
  144.         case OP_REPC:
  145.             ldelete(1L, FALSE);
  146.             /* fall into the next case! */
  147.  
  148.         case OP_DELC:
  149.             if (up->undo_obj.obj_char == '\r') {
  150.                 count = up->count;
  151.                 while (count--)
  152.                     status = lnewline();
  153.             } else
  154.                 status = linsert(up->count, up->undo_obj.obj_char);
  155.             break;
  156.  
  157.         case OP_DSTR:
  158.             status = linstr(up->undo_obj.obj_string);
  159.             break;
  160.  
  161.         case OP_INSC:
  162.         case OP_ISTR:
  163.             status = ldelete(up->count, FALSE);
  164.             break;
  165.     }
  166.  
  167.     /* update the buffer undo count */
  168.     curwp->w_bufp->undo_count--;
  169.  
  170.     /* and discard the undo entry */
  171.     curwp->w_bufp->undo_head = up->next;
  172.     free((char *)up);
  173.     undoing = FALSE;
  174.     return(status);
  175. }
  176.  
  177. /* clear and deallocate the contents of a buffer's undo stack */
  178.  
  179. VOID undo_zot(bp)
  180.  
  181. BUFFER *bp;
  182.  
  183. {
  184.     UNDO_OBJ *up;    /* current undo object being cleared */
  185.     UNDO_OBJ *np;    /* next undo object to be cleared */
  186.  
  187.     bp->undo_count = 0L;
  188.     np = bp->undo_head;
  189.     while (np != (UNDO_OBJ *)NULL) {
  190.  
  191.         /* advance to the next undo object */
  192.         up = np;
  193.         np = up->next;
  194.  
  195.         /* and clear it */
  196.         free((char *)up);
  197.     }
  198.  
  199.     /* and tell the buffer it's gone */
  200.     bp->undo_head = (UNDO_OBJ *)NULL;
  201. }
  202.  
  203. /* Undo last done command */
  204.  
  205. int PASCAL NEAR undo(f, n)
  206.  
  207. int f,n;    /* prefix flag and argument */
  208.  
  209. {
  210.     int status;    /* status return from undone operations */
  211.     UNDO_OBJ *up;    /* ptr to the undo object */
  212.  
  213.     /* handle the default argument */
  214.     if (f == FALSE)
  215.         n = 1;
  216.  
  217.     /* non-positive arguments loose */
  218.     if (n < 1)
  219.         return(FALSE);
  220.  
  221.     /* repeat a single undo for n iterations */
  222.     status = TRUE;
  223.     up = curwp->w_bufp->undo_head;
  224.     while ((status == TRUE) && (n > 0) && up != (UNDO_OBJ *)NULL) {
  225.  
  226.         /* undo individual operations until the next statement boundry */
  227.         do {
  228.             status = undo_op();
  229.             up = curwp->w_bufp->undo_head;
  230.         } while ((status == TRUE) &&
  231.              (up != (UNDO_OBJ *)NULL) &&
  232.              (up->type != OP_CMND));
  233.  
  234.         /* one less rep to do... */
  235.         n--;
  236.     }
  237.     return(status);
  238. }
  239.  
  240. /* delete current buffer's undo stack */
  241.  
  242. int PASCAL NEAR undo_delete(f, n)
  243.  
  244. int f,n;    /* prefix flag and argument */
  245.  
  246. {
  247.     /* well, do that..... and we know it will work.... */
  248.     undo_zot(curbp);
  249.     return(TRUE);   
  250. }
  251.  
  252. /* pop up a list of the current buffer's undo stack */
  253.  
  254. int PASCAL NEAR undo_list(f, n)
  255.  
  256. int f,n;    /* prefix flag and argument */
  257.  
  258. {
  259.     register int status;    /* stutus return */
  260.  
  261.     if ((status = undolist()) != TRUE)
  262.         return(status);
  263.     return(wpopup(ulistp));
  264. }
  265.  
  266. PASCAL NEAR undolist()
  267.  
  268. {
  269.     register char *cp1;    /* scanning pointer into line to build */
  270.     register char *cp2;
  271.     long count;        /* number of remaining undo elements in stack */
  272.     UNDO_OBJ *up;        /* current undo object being listed */
  273.     char b[8];        /* place to build ascii version of longs */
  274.     char line[128];        /* text buffer to hold current line */
  275.     int status;        /* return status from subcommands */
  276.  
  277.     /* prepare and clear the buffer holding the undo list */
  278.     ulistp->b_flag &= ~BFCHG;        /* Don't complain!    */
  279.     if ((status = bclear(ulistp)) != TRUE)     /* Blow old text away    */
  280.         return(status);
  281.     strcpy(ulistp->b_fname, "");
  282.  
  283.     /* add in the header text */
  284.     if (addline(ulistp,
  285.         "           Line/Pos  REP   Type  Data"
  286.         ) == FALSE
  287.     || addline(ulistp, "           --------  ---   ----  ----") == FALSE)
  288.         return(FALSE);
  289.  
  290.     /* scan through the undo stack, starting at the top! */
  291.     up    = curwp->w_bufp->undo_head;
  292.     count = curwp->w_bufp->undo_count;
  293.     while (up != NULL) {
  294.  
  295.         /* Starting at the beginning of the line */
  296.         cp1 = &line[0];
  297.  
  298.         /* add in the undo stack ordinal number */
  299.         flong_asc(b, 7, count);
  300.             cp2 = &b[0];
  301.         while (*cp2)
  302.             *cp1++ = *cp2++;
  303.         *cp1++ = ' ';
  304.  
  305.         /* and then the line number */
  306.         flong_asc(b, 7, up->line_num);
  307.         cp2 = &b[0];
  308.         while (*cp2)
  309.             *cp1++ = *cp2++;
  310.         *cp1++ = '/';
  311.  
  312.         /* and the offset into the line */
  313.         strcpy(b, int_asc(up->offset));
  314.         while (strlen(b) < 6)
  315.             strcat(b, " ");
  316.         cp2 = &b[0];
  317.         while (*cp2)
  318.             *cp1++ = *cp2++;
  319.         *cp1++ = ' ';
  320.  
  321.         /* and the count */
  322.         strcpy(b, int_asc(up->count));
  323.         while (strlen(b) < 3)
  324.             strcat(b, " ");
  325.         cp2 = &b[0];
  326.         while (*cp2)
  327.             *cp1++ = *cp2++;
  328.         *cp1++ = ' ';            /* Gap.         */
  329.  
  330.         /* next, the undo type */
  331.         switch (up->type) {
  332.     
  333.             case OP_CMND:
  334.                 strcpy(cp1, "CMND  ");
  335.                 break;        
  336.     
  337.             case OP_CPOS:
  338.                 strcpy(cp1, "CPOS  ");
  339.                 break;        
  340.     
  341.             case OP_DELC:
  342.                 strcpy(cp1, "DELC  ");
  343.                 cmdstr(up->undo_obj.obj_char, cp1 + 6);
  344.                 break;
  345.  
  346.             case OP_DSTR:
  347.                 strcpy(cp1, "DSTR  ");
  348.                 strcat(cp1, "\"");
  349.                 strncat(cp1, up->undo_obj.obj_string, 40);
  350.                 cp1[26] = '+';
  351.                 cp1[27] = 0;
  352.                 strcat(cp1, "\"");
  353.                 break;
  354.  
  355.             case OP_INSC:
  356.                 strcpy(cp1, "INSC  ");
  357.                 cmdstr(up->undo_obj.obj_char, cp1 + 6);
  358.                 break;
  359.  
  360.             case OP_ISTR:
  361.                 strcpy(cp1, "ISTR  ");
  362.                 break;        
  363.  
  364.             case OP_REPC:
  365.                 strcpy(cp1, "REPC  ");
  366.                 cmdstr(up->undo_obj.obj_char, cp1 + 6);
  367.                 break;        
  368.         }
  369.  
  370.         /* terminate and add the built line into the buffer */
  371.         if (addline(ulistp, line) == FALSE)
  372.             return(FALSE);
  373.  
  374.         /* on to the next undo! */
  375.         count--;
  376.         up = up->next;
  377.     }
  378.     return(TRUE);
  379. }
  380.  
  381. /* clear ALL the undo stacks */
  382.  
  383. #if    PROTO
  384. VOID undo_dump(void)
  385. #else
  386. VOID undo_dump()
  387. #endif
  388. {
  389.     BUFFER *bp;
  390.  
  391.     /* clear all the buffer's undo stacks */
  392.     bp = bheadp;
  393.     while (bp) {
  394.         undo_zot(bp);
  395.         bp = bp->b_bufp;
  396.     }
  397. }
  398.  
  399. /*    ROOM:    Allocate memory using malloc()
  400.         on failure, discard oldest undo information
  401.         and retry
  402. */
  403.  
  404. #if    PROTO
  405. VOID *room(int nbytes)
  406. #else
  407. VOID *room(nbytes)
  408.  
  409. int nbytes;    /* number of bytes to malloc() */
  410. #endif
  411. {
  412.     void *ptr;    /* temporary pointer */
  413.     BUFFER *bp;    /* buffer to dealloc memory from */
  414.     UNDO_OBJ *up;    /* ptr to undo struct to free */
  415.     UNDO_OBJ *lp;    /* last undo struct before up */
  416.  
  417.     ptr = (char *)NULL;
  418.     while (ptr == (char *)NULL) {
  419.  
  420.         /* attempt to allocate the memory */
  421.         ptr = malloc(nbytes);
  422.         if (ptr != (char *)NULL)
  423.             return(ptr);
  424.  
  425.         /* find the oldest visited buffer */
  426. nextbuf:    bp = getoldb();
  427.  
  428.         /* no buffers left to check? */
  429.         if (bp == (BUFFER *)NULL)
  430.             return((char *)NULL);
  431.  
  432.         /* any undo info to discard? */
  433.         if (bp->undo_count == 0) {
  434.             bp->last_access = 0;
  435.             goto nextbuf;
  436.         }
  437.  
  438.         /* dump the last undo structure */
  439.         lp = (UNDO_OBJ *)NULL;
  440.         up = bp->undo_head;
  441.         while (up->next != (UNDO_OBJ *)NULL) {
  442.             lp = up;
  443.             up = up->next;
  444.         }
  445.  
  446.         /* dump the oldest undo */
  447.         free((char *)up);
  448.         lp->next = (UNDO_OBJ *)NULL;
  449.         bp->undo_count--;
  450.     }
  451. }
  452.  
  453. /*    RE-ROOM: Allocate memory using realloc()
  454.         on failure, discard oldest undo information
  455.         and retry
  456. */
  457.  
  458. #if    PROTO
  459. VOID *reroom(void *orig_ptr, int nbytes)
  460. #else
  461. VOID *reroom(orig_ptr, nbytes)
  462.  
  463. int nbytes;    /* number of bytes to malloc() */
  464. void *orig_ptr;
  465. #endif
  466. {
  467.     void *ptr;    /* temporary pointer */
  468.     BUFFER *bp;    /* buffer to dealloc memory from */
  469.     UNDO_OBJ *up;    /* ptr to undo struct to free */
  470.     UNDO_OBJ *lp;    /* last undo struct before up */
  471.  
  472.     /*
  473.      * Avoid the whole problem of non-ANSI realloc() functions
  474.      * that don't handle NULL pointers correctly by calling
  475.      * malloc() (by way of room()) directly if orig_ptr is NULL.
  476.      */
  477.     if (orig_ptr == NULL)
  478.         return(room(nbytes));
  479.  
  480.     ptr = (char *)NULL;
  481.     while (ptr == (char *)NULL) {
  482.  
  483.         /* attempt to allocate the memory */
  484.         ptr = realloc(orig_ptr, nbytes);
  485.         if (ptr != (char *)NULL)
  486.             return(ptr);
  487.  
  488.         /* find the oldest visited buffer */
  489. nxtbuf:        bp = getoldb();
  490.  
  491.         /* no buffers left to check? */
  492.         if (bp == (BUFFER *)NULL)
  493.             return((char *)NULL);
  494.  
  495.         /* any undo info to discard? */
  496.         if (bp->undo_count == 0) {
  497.             bp->last_access = 0;
  498.             goto nxtbuf;
  499.         }
  500.  
  501.         /* dump the last undo structure */
  502.         lp = (UNDO_OBJ *)NULL;
  503.         up = bp->undo_head;
  504.         while (up->next != (UNDO_OBJ *)NULL) {
  505.             lp = up;
  506.             up = up->next;
  507.         }
  508.  
  509.         /* dump the oldest undo */
  510.         free((char *)up);
  511.         lp->next = (UNDO_OBJ *)NULL;
  512.         bp->undo_count--;
  513.     }
  514. }
  515.  
  516.