[BACK]Return to undo.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mg

Diff for /src/usr.bin/mg/undo.c between version 1.8 and 1.9

version 1.8, 2002/03/16 20:29:21 version 1.9, 2002/03/18 01:45:55
Line 41 
Line 41 
  * Global variables   * Global variables
  */   */
 /*  /*
  * undo_disable_flag -   * undo_disable_flag: Stop doing undo (useful when we know are
  *   *      going to deal with huge deletion/insertions
  * Stop doing undo (useful when we know are   *      that we don't plan to undo)
  * going to deal with huge deletion/insertions  
  * that we don't plan to undo)  
  */   */
 int undo_disable_flag;  int undo_disable_flag;
 int undoaction;                 /* Are we called indirectly from undo()? */  
   
 /*  /*
  * Local functions   * Local functions
  */   */
 static int find_offset(LINE *, int);  static int find_absolute_dot(LINE *, int);
 static int find_linep(int, LINE **, int *);  static int find_line_offset(int, LINE **, int *);
 static struct undo_rec *new_undo_record(void);  static struct undo_rec *new_undo_record(void);
 static int drop_oldest_undo_record(void);  static int drop_oldest_undo_record(void);
   
   /*
    * find_{absolute_dot,line_offset}()
    *
    * Find an absolute dot in the buffer from a line/offset pair, and vice-versa.
    *
    * Since lines can be deleted while they are referenced by undo record, we
    * need to have an absolute dot to have something reliable.
    */
   
 static int  static int
 find_offset(LINE *lp, int off)  find_absolute_dot(LINE *lp, int off)
 {  {
         int count = 0;          int count = 0;
         LINE *p;          LINE *p;
Line 68 
Line 74 
                 if (count != 0) {                  if (count != 0) {
                         if (p == curwp->w_linep) {                          if (p == curwp->w_linep) {
                                 ewprintf("Error: Undo stuff called with a"                                  ewprintf("Error: Undo stuff called with a"
                                     "nonexistent line\n");                                      "nonexistent line");
                                 return FALSE;                                  return FALSE;
                         }                          }
                 }                  }
Line 80 
Line 86 
 }  }
   
 static int  static int
 find_linep(int pos, LINE **olp, int *offset)  find_line_offset(int pos, LINE **olp, int *offset)
 {  {
         LINE *p;          LINE *p;
   
         p = curwp->w_linep;          p = curwp->w_linep;
         while (pos > 0 && pos > llength(p)) {          while (pos > llength(p)) {
                 pos -= llength(p) + 1;                  pos -= llength(p) + 1;
                 if ((p = lforw(p)) == curwp->w_linep) {                  if ((p = lforw(p)) == curwp->w_linep) {
                         *olp = NULL;                          *olp = NULL;
Line 105 
Line 111 
         struct undo_rec *rec;          struct undo_rec *rec;
   
         rec = LIST_FIRST(&undo_free);          rec = LIST_FIRST(&undo_free);
         if (rec != NULL)          if (rec != NULL) {
                 LIST_REMOVE(rec, next); /* Remove it from the free-list */                  LIST_REMOVE(rec, next); /* Remove it from the free-list */
         else {                  undo_free_num--;
           } else {
                 if ((rec = malloc(sizeof *rec)) == NULL)                  if ((rec = malloc(sizeof *rec)) == NULL)
                         panic("Out of memory in undo code (record)");                          panic("Out of memory in undo code (record)");
         }          }
         memset(rec, 0, sizeof(struct undo_rec));          memset(rec, 0, sizeof(struct undo_rec));
   
         return rec;          return rec;
 }  }
   
 void  void
 free_undo_record(struct undo_rec *rec)  free_undo_record(struct undo_rec *rec)
 {  {
           static int initialised = 0;
   
           /*
            * On the first run, do initialisation of the free list.
            */
           if (initialised == 0) {
                   LIST_INIT(&undo_free);
                   initialised = 1;
           }
         if (rec->content != NULL) {          if (rec->content != NULL) {
                 free(rec->content);                  free(rec->content);
                 rec->content = NULL;                  rec->content = NULL;
Line 128 
Line 144 
                 return;                  return;
         }          }
         undo_free_num++;          undo_free_num++;
   
         LIST_INSERT_HEAD(&undo_free, rec, next);          LIST_INSERT_HEAD(&undo_free, rec, next);
 }  }
   
Line 141 
Line 157 
 {  {
         struct undo_rec *rec;          struct undo_rec *rec;
   
         rec = LIST_END(&undo_list);          rec = LIST_END(&curbp->b_undo);
         if (rec != NULL) {          if (rec != NULL) {
                 undo_free_num--;                  undo_free_num--;
                 LIST_REMOVE(rec, next); /* Remove it from the undo_list before                  LIST_REMOVE(rec, next);
                                          * we insert it in the free list */  
                 free_undo_record(rec);                  free_undo_record(rec);
                 return 1;                  return 1;
         }          }
         return 0;          return 0;
 }  }
   
 int  static __inline__ int
 undo_init(void)  last_was_boundary()
 {  {
         LIST_INIT(&undo_free);          struct undo_rec *rec;
   
         return TRUE;          if ((rec = LIST_FIRST(&curbp->b_undo)) != NULL &&
               (rec->type == BOUNDARY))
                   return 1;
           return 0;
 }  }
   
 int  int
 undo_enable(int on)  undo_enable(int on)
 {  {
         undo_disable_flag = on ? 0 : 1;          undo_disable_flag = on ? 0 : 1;
   
         /*  
          * XXX-Vince:  
          *  
          * Here, I wonder if we should assume that the user has made a  
          * long term choice.  If so, we could free all internal undo  
          * data and save memory.  
          */  
   
         return on;          return on;
 }  }
   
 int  int
 undo_add_custom(int type, LINE *lp, int offset, void *content, int size)  undo_add_boundary(void)
 {  {
         struct undo_rec *rec;          struct undo_rec *rec;
   
         if (undo_disable_flag)  
                 return TRUE;  
         rec = new_undo_record();          rec = new_undo_record();
         rec->pos = find_offset(lp, offset);          rec->type = BOUNDARY;
         rec->type = type;  
         rec->content = content;  
         rec->region.r_linep = lp;  
         rec->region.r_offset = offset;  
         rec->region.r_size = size;  
   
         LIST_INSERT_HEAD(&curbp->b_undo, rec, next);          LIST_INSERT_HEAD(&curbp->b_undo, rec, next);
   
         return TRUE;          return TRUE;
 }  }
   
   /*
    * If asocial is true, we arrange for this record to be let alone.  forever.
    * Yes, this is a bit of a hack...
    */
 int  int
 undo_add_boundary(void)  undo_add_custom(int asocial,
       int type, LINE *lp, int offset, void *content, int size)
 {  {
         struct undo_rec *rec;          struct undo_rec *rec;
   
         if (undo_disable_flag)          if (undo_disable_flag)
                 return TRUE;                  return TRUE;
   
         rec = new_undo_record();          rec = new_undo_record();
         rec->type = BOUNDARY;          if (lp != NULL)
                   rec->pos = find_absolute_dot(lp, offset);
           else
                   rec->pos = 0;
           rec->type = type;
           rec->content = content;
           rec->region.r_linep = lp;
           rec->region.r_offset = offset;
           rec->region.r_size = size;
   
           if (!last_was_boundary())
                   undo_add_boundary();
         LIST_INSERT_HEAD(&curbp->b_undo, rec, next);          LIST_INSERT_HEAD(&curbp->b_undo, rec, next);
           undo_add_boundary();
           if (asocial)            /* Add a second one */
                   undo_add_boundary();
   
         return TRUE;          return TRUE;
 }  }
   
Line 217 
Line 237 
 {  {
         REGION reg;          REGION reg;
         struct undo_rec *rec;          struct undo_rec *rec;
           int pos;
   
         if (undo_disable_flag)          if (undo_disable_flag)
                 return TRUE;                  return TRUE;
   
         reg.r_linep = lp;          reg.r_linep = lp;
         reg.r_offset = offset;          reg.r_offset = offset;
         reg.r_size = size;          reg.r_size = size;
   
           pos = find_absolute_dot(lp, offset);
   
         /*          /*
          * We try to reuse the last undo record to `compress' things.           * We try to reuse the last undo record to `compress' things.
          */           */
         rec = LIST_FIRST(&curbp->b_undo);          rec = LIST_FIRST(&curbp->b_undo);
         if ((rec != NULL) &&          /* this will be hit like, 80% of the time... */
             (rec->type == INSERT) &&          if (rec != NULL && rec->type == BOUNDARY)
             (rec->region.r_linep == lp)) {                  rec = LIST_NEXT(rec, next);
                 int dist;  
   
                 dist = rec->region.r_offset - reg.r_offset;          if ((rec != NULL) &&
               (rec->type == INSERT)) {
                 if (rec->region.r_size >= dist) {                  if (rec->pos + rec->region.r_size == pos) {
                         rec->region.r_size += reg.r_size;                          rec->region.r_size += reg.r_size;
                         return TRUE;                          return TRUE;
                 }                  }
         }          }
   
         /*          /*
          * We couldn't reuse the last undo record, so prepare a new one           * We couldn't reuse the last undo record, so prepare a new one
          */           */
         rec = new_undo_record();          rec = new_undo_record();
         rec->pos = find_offset(lp, offset);          rec->pos = pos;
         rec->type = INSERT;          rec->type = INSERT;
         memmove(&rec->region, &reg, sizeof(REGION));          memmove(&rec->region, &reg, sizeof(REGION));
         rec->content = NULL;          rec->content = NULL;
   
           if (!last_was_boundary())
                   undo_add_boundary();
   
         LIST_INSERT_HEAD(&curbp->b_undo, rec, next);          LIST_INSERT_HEAD(&curbp->b_undo, rec, next);
           undo_add_boundary();
   
         return TRUE;          return TRUE;
 }  }
Line 263 
Line 289 
 {  {
         REGION reg;          REGION reg;
         struct undo_rec *rec;          struct undo_rec *rec;
         int dist, pos, skip = 0;          int pos;
   
         if (undo_disable_flag)          if (undo_disable_flag)
                 return TRUE;                  return TRUE;
Line 272 
Line 298 
         reg.r_offset = offset;          reg.r_offset = offset;
         reg.r_size = size;          reg.r_size = size;
   
         pos = find_offset(lp, offset);          pos = find_absolute_dot(lp, offset);
   
         if (size == 1 && llength(lp) == 0) {          if (offset == llength(lp)) /* if it's a newline... */
                 skip = 1;                  undo_add_boundary();
           else if ((rec = LIST_FIRST(&curbp->b_undo)) != NULL) {
                   /*
                    * Separate this command from the previous one if we're not
                    * just before the previous record...
                    */
                   if (rec->type == DELETE){
                           if (rec->pos - rec->region.r_size != pos)
                                   undo_add_boundary();
                   } else if (rec->type != BOUNDARY)
                           undo_add_boundary();
         }          }
   
         /*  
          * Again, try to reuse last undo record, if we can  
          */  
         rec = LIST_FIRST(&curbp->b_undo);  
         if (!skip &&  
             (rec != NULL) &&  
             (rec->type == DELETE) &&  
             (rec->region.r_linep == reg.r_linep)) {  
                 char *newbuf;  
                 int newlen;  
   
                 dist = rec->region.r_offset - reg.r_offset;  
                 if (rec->region.r_size >= dist) {  
                         newlen = rec->region.r_size + reg.r_size;  
   
                         do {  
                                 newbuf = malloc(newlen * sizeof(char));  
                         } while (newbuf == NULL && drop_oldest_undo_record());  
   
                         if (newbuf == NULL)  
                                 panic("out of memory in undo delete code");  
   
                         /*  
                          * [new data][old data]  
                          */  
                         region_get_data(&reg, newbuf, size);  
                         memmove(newbuf + reg.r_size, rec->content,  
                             rec->region.r_size);  
   
                         rec->pos = pos;  
                         rec->region.r_offset = reg.r_offset;  
                         rec->region.r_size = newlen;  
                         if (rec->content != NULL)  
                                 free(rec->content);  
                         rec->content = newbuf;  
   
                         return TRUE;  
                 }  
         }  
   
         /*  
          * So we couldn't reuse the last undo record? Just allocate a new  
          * one.  
          */  
         rec = new_undo_record();          rec = new_undo_record();
         rec->pos = pos;          rec->pos = pos;
   
Line 330 
Line 321 
         do {          do {
                 rec->content = malloc(reg.r_size + 1);                  rec->content = malloc(reg.r_size + 1);
         } while ((rec->content == NULL) && drop_oldest_undo_record());          } while ((rec->content == NULL) && drop_oldest_undo_record());
   
         if (rec->content == NULL)          if (rec->content == NULL)
                 panic("Out of memory");                  panic("Out of memory");
   
         region_get_data(&reg, rec->content, reg.r_size);          region_get_data(&reg, rec->content, reg.r_size);
   
         LIST_INSERT_HEAD(&curbp->b_undo, rec, next);          LIST_INSERT_HEAD(&curbp->b_undo, rec, next);
           undo_add_boundary();
   
         return TRUE;          return TRUE;
 }  }
Line 350 
Line 342 
         REGION reg;          REGION reg;
         struct undo_rec *rec;          struct undo_rec *rec;
   
   
         if (undo_disable_flag)          if (undo_disable_flag)
                 return TRUE;                  return TRUE;
   
         reg.r_linep = lp;          reg.r_linep = lp;
         reg.r_offset = offset;          reg.r_offset = offset;
         reg.r_size = size;          reg.r_size = size;
   
         rec = new_undo_record();          rec = new_undo_record();
         rec->pos = find_offset(lp, offset);          rec->pos = find_absolute_dot(lp, offset);
         rec->type = CHANGE;          rec->type = CHANGE;
         memmove(&rec->region, &reg, sizeof reg);          memmove(&rec->region, &reg, sizeof reg);
   
           /*
            * Try to allocate a buffer for the changed data.
            */
         do {          do {
                 rec->content = malloc(size + 1);                  rec->content = malloc(size + 1);
         } while ((rec->content == NULL) && drop_oldest_undo_record());          } while ((rec->content == NULL) && drop_oldest_undo_record());
   
         if (rec->content == NULL)          if (rec->content == NULL)
                 panic("Out of memory in undo change code");                  panic("Out of memory in undo change code");
   
         region_get_data(&reg, rec->content, size);          region_get_data(&reg, rec->content, size);
   
           if (!last_was_boundary())
                   undo_add_boundary();
         LIST_INSERT_HEAD(&curbp->b_undo, rec, next);          LIST_INSERT_HEAD(&curbp->b_undo, rec, next);
           undo_add_boundary();
   
         return TRUE;          return TRUE;
 }  }
   
Line 394 
Line 391 
          */           */
         if ((bp = bfind("*undo*", TRUE)) == NULL)          if ((bp = bfind("*undo*", TRUE)) == NULL)
                 return FALSE;                  return FALSE;
           bp->b_flag |= BFREADONLY;
         bclear(bp);          bclear(bp);
         popbuf(bp);          popbuf(bp);
   
Line 431 
Line 428 
         return TRUE;          return TRUE;
 }  }
   
   /*
    * After the user did action1, then action2, then action3 :
    *
    *      [action3] <--- Undoptr
    *      [action2]
    *      [action1]
    *       ------
    *       [undo]
    *
    * After undo:
    *
    *      [undo of action3]
    *      [action2] <--- Undoptr
    *      [action1]
    *       ------
    *       [undo]
    *
    * After another undo:
    *
    *
    *      [undo of action2]
    *      [undo of action3]
    *      [action1]  <--- Undoptr
    *       ------
    *       [undo]
    *
    * Note that the "undo of actionX" have no special meaning. Only when,
    * say, we undo a deletion, the insertion will be recorded just as if it
    * was typed on the keyboard. Hence resulting in the inverse operation to be
    * saved in the list.
    *
    * If undoptr reaches the bottom of the list, or if we moved between
    * two undo actions, we make it point back at the topmost record. This is
    * how we handle redoing.
    */
 int  int
 undo(int f, int n)  undo(int f, int n)
 {  {
         struct undo_rec *rec;          struct undo_rec *ptr, *nptr;
         LINE *ln;          int done, rval;
         int off;          LINE *lp;
           int offset;
   
         /*          ptr = curbp->b_undoptr;
          * Let called functions know they are below us (for  
          * example, ldelete don't want to record an undo record  
          * when called by us)  
          */  
         undoaction++;  
   
           /* if we moved, make ptr point back to the top of the list */
           if ((curbp->b_undopos.r_linep != curwp->w_dotp) ||
               (curbp->b_undopos.r_offset != curwp->w_doto) ||
               (ptr == NULL))
                   ptr = LIST_FIRST(&curbp->b_undo);
   
           rval = TRUE;
         while (n > 0) {          while (n > 0) {
                 rec = LIST_FIRST(&curbp->b_undo);                  /* if we have a spurious boundary, free it and move on.... */
                 if (rec == NULL) {                  while (ptr && ptr->type == BOUNDARY) {
                         ewprintf("Nothing to undo!");                          nptr = LIST_NEXT(ptr, next);
                         return FALSE;                          LIST_REMOVE(ptr, next);
                           free_undo_record(ptr);
                           ptr = nptr;
                 }                  }
                 LIST_REMOVE(rec, next);  
                 if (rec->type == BOUNDARY) {  
                         continue;  
                 }  
   
                 find_linep(rec->pos, &ln, &off);  
                 if (ln == NULL)  
                         return FALSE;  
   
                 /*                  /*
                  * Move to where this record has to apply                   * Ptr is NULL, but on the next run, it will point to the
                    * top again, redoing all stuff done in the buffer since
                    * its creation.
                  */                   */
                 curwp->w_dotp = ln;                  if (ptr == NULL) {
                 curwp->w_doto = off;                          ewprintf("Nothing to undo!");
                           rval = FALSE;
                 switch (rec->type) {  
                 case INSERT:  
                         ldelete(rec->region.r_size, KFORW);  
                         break;                          break;
                 case DELETE:  
                         region_put_data(rec->content, rec->region.r_size);  
                         break;  
                 case CHANGE:  
                         forwchar(0, rec->region.r_size);  
                         lreplace(rec->region.r_size, rec->content, 1);  
                         break;  
                 default:  
                         break;  
                 }                  }
   
                 free_undo_record(rec);  
   
                   /*
                    * Loop while we don't get a boundary specifying we've
                    * finished the current action...
                    */
                   done = 0;
                   do {
                           /* Unlink the current node from the list */
                           nptr = LIST_NEXT(ptr, next);
                           LIST_REMOVE(ptr, next);
   
                           /*
                            * Move to where this has to apply
                            *
                            * Boundaries are put as position 0 (to save
                            * lookup time in find_absolute_dot) so we must
                            * not move there...
                            */
                           if (ptr->type != BOUNDARY) {
                                   if (find_line_offset(ptr->pos,&lp,&offset)
                                       == FALSE) {
                                           ewprintf("Internal error in Undo!");
                                           rval = FALSE;
                                           break;
                                   }
                                   curwp->w_dotp = lp;
                                   curwp->w_doto = offset;
                           }
   
                           /*
                            * Do operation^-1
                            */
                           switch (ptr->type) {
                           case INSERT:
                                   ldelete(ptr->region.r_size, KFORW);
                                   break;
                           case DELETE:
                                   region_put_data(ptr->content,
                                       ptr->region.r_size);
                                   break;
                           case CHANGE:
                                   forwchar(0, ptr->region.r_size);
                                   lreplace(ptr->region.r_size,
                                       ptr->content, 1);
                                   break;
                           case BOUNDARY:
                                   done = 1;
                                   break;
                           default:
                                   break;
                           }
                           free_undo_record(ptr);
   
                           /* And move to next record */
                           ptr = nptr;
                   } while (ptr != NULL && !done);
   
                   ewprintf("Undo!");
                 n--;                  n--;
         }          }
         undoaction--;          /*
            * Record where we are. (we have to save our new position at the end
         return TRUE;           * since we change the dot when undoing....)
            */
           curbp->b_undoptr = ptr;
           curbp->b_undopos.r_linep = curwp->w_dotp;
           curbp->b_undopos.r_offset = curwp->w_doto;
   
           return rval;
 }  }

Legend:
Removed from v.1.8  
changed lines
  Added in v.1.9