1 /* ********************************************
  2    *                                          *
  3    * Copyright, (C) Honeywell Bull Inc., 1988 *
  4    *                                          *
  5    ******************************************** */
  6 
  7 /* HISTORY COMMENTS:
  8   1) change(88-08-04,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  9      install(88-10-12,MR12.2-1160):
 10      Created.
 11   2) change(88-08-09,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
 12      install(88-10-12,MR12.2-1160):
 13      Fix references to include files; "wstdefs.h" was split into
 14      "wstdefs.h" and "wsttype.h" to avoid declaration clash; also,
 15      various constants defined to descriptive names.
 16   3) change(89-01-18,Lee), approve(89-01-02,MCR8043), audit(89-03-14,Flegel),
 17      install(89-04-24,MR12.3-1033):
 18      phx21233 - Modified line input manipulations to update the character
 19      display length flags as well to control echoing of echoed and non-echoed
 20      input.
 21                                                    END HISTORY COMMENTS */
 22 
 23 #include <dos.h>
 24 #include "wstdefs.h"
 25 #include "wstglob.h"
 26 #include "wstkill.h"
 27 
 28 /*^L
 29 **********************************************************************
 30 
 31   Routine:            YANK
 32 
 33   Function:
 34       This routine yanks the last item that was saved to the kill
 35   buffer and inserts the item into the edit mode line buffer. The
 36   display is updated.
 37 
 38   Parameters:
 39      (input/output)   line - specifies the structure containing the
 40                           line being edited and information about the
 41                           line in edit mode
 42 
 43   Returns:            NONE
 44 
 45   Note 1 - If an error occurs (e.g. there is not enough room in the
 46       line to contain the item being yanked), a beep is sounded and
 47       no processing takes place.
 48 **********************************************************************/
 49 
 50 yank(line)
 51 EDIT_LINE *line;
 52 {
 53     int line_chars_free;
 54     int i, j;
 55     int hi, lo;
 56     int cnt;
 57     int previous;
 58 
 59     /* determine amount of free space in line being edited */
 60     line_chars_free = MAX_LINE_SIZE - line->length;
 61 
 62     /* check and handle for nothing in kill buffer */
 63     if (kill_info.chars_free == KILL_BUFF_SIZE ||
 64         (kill_info.current == kill_info.tail && !kill_info.head_of_killbuff)) {
 65         beep();
 66         return;
 67     }
 68 
 69     /* index to previous item in kill buffer */
 70     previous = (kill_info.current + KILL_BUFF_SIZE - kill_info.current_size) %
 71         KILL_BUFF_SIZE;
 72 
 73     i = previous;
 74 
 75     /* determine total size of this previous item */
 76     lo = kill_info.kb[i];
 77     i = (i+1)%KILL_BUFF_SIZE;
 78     hi = kill_info.kb[i];
 79     i = (i+1)%KILL_BUFF_SIZE;
 80 
 81     cnt = (hi * MAX_7_BIT_VAL) + lo;
 82 
 83     /* subtract item size bytes to get number of data bytes */
 84     cnt -= TOTAL_SIZE_SPEC_BYTES;
 85 
 86     /* check and handle insufficient space in line to insert item */
 87     if (line_chars_free < cnt) {
 88         beep();
 89         return;
 90     }
 91 
 92     /* create gap for inserting yank item */
 93     for (j = line->length; j >= line->index; j--) {
 94         line->line[j+cnt] = line->line[j];
 95         line->size[j+cnt] = line->size[j];    /* phx21233 R.L. - update display length flags */
 96     }
 97 
 98     line->length += cnt;
 99     kill_info.yank_index = line->index;
100     kill_info.yank_size = cnt;    /* reset on save or RETURN */
101 
102     /* copy item into gap */
103     for (j = 0; j < cnt; j++) {
104         line->line[line->index+j] = kill_info.kb[i];
105         line->size[line->index+j] = (kb.echo) ? TRUE : FALSE; /* phx21233 R.L. updated echo status */
106         i = (i+1)%KILL_BUFF_SIZE;
107     }
108 
109     /* update the display and cursor */
110     redisplay(line,line->index,&(line->max_row),&(line->max_col));
111     cursor_right(line,kill_info.yank_size);
112 }
113 
114 
115 
116 /*^L
117 **********************************************************************
118 
119   Routine:            YANK_PREVIOUS
120 
121   Function:
122       This routine allows each item in the kill buffer to be yanked
123   in reversed chronological order, each time the routine is called.
124   The routine "yank()" must have been called initially to yank the
125   most recently killed item in order for previous items to be yanked.
126   When the previous item is yanked, it replaces the item that was
127   yanked immediately before by deleting that item first before
128   inserting the newly yanked item. The display and cursor is updated
129   with each yank.
130 
131   Parameters:
132      (input/output)   line - specifies the structure containing the
133                           line being edited and information about the
134                           line in edit mode
135 
136   Returns:            NONE
137 
138   Note 1 - If an error occurs (e.g. there is no room in the line
139       being edited to contain the item yanked), the routine will
140       sound a beep and no further processing takes place.
141 **********************************************************************/
142 
143 yank_previous(line)
144 EDIT_LINE *line;
145 {
146     int i, j;
147     int hi, lo;
148     int cnt;
149     int previous;
150     int diff;
151 
152     /* check that a yank was previously made */
153     if (kill_info.yank_size < 1) {
154         beep();
155         return;
156     }
157 
158     /* check that cursor has not moved since last yank */
159     if (line->index != kill_info.yank_index+kill_info.yank_size) {
160         beep();
161         return;
162     }
163 
164     /* check and handle for nothing in the kill buffer */
165     if (kill_info.chars_free == KILL_BUFF_SIZE ||
166         (kill_info.current == kill_info.tail && !kill_info.head_of_killbuff)) {
167         beep();
168         return;
169     }
170 
171     /* get index to previous item in the kill buffer */
172     previous = (kill_info.current + KILL_BUFF_SIZE -
173         kill_info.current_size) % KILL_BUFF_SIZE;
174 
175     /* check and handle reaching last item of kill buffer */
176     if (previous == kill_info.tail)
177         previous = kill_info.head;
178 
179     /* index of current item now indexes to previous item */
180     kill_info.current = previous;
181 
182     /* update size of current item with size of previous item */
183     if (previous != kill_info.tail) {
184 
185         /* get size of previous item */
186         previous = (previous + KILL_BUFF_SIZE - N_SIZE_SPEC_BYTES) % KILL_BUFF_SIZE;
187         lo = kill_info.kb[previous];
188         previous = (previous+1) % KILL_BUFF_SIZE;
189         hi = kill_info.kb[previous];
190         kill_info.current_size = (hi * MAX_7_BIT_VAL)+lo;
191     }
192 
193     /* current does not index to first item of kill buffer */
194     kill_info.head_of_killbuff = FALSE;
195 
196     /* index to previous item */
197     previous = (kill_info.current + KILL_BUFF_SIZE -
198         kill_info.current_size) % KILL_BUFF_SIZE;
199 
200     /* move cursor to beginning of previously yanked item */
201     cursor_left(line,kill_info.yank_size);
202 
203     i = previous;
204 
205     /* get size of item to yank */
206     lo = kill_info.kb[i];
207     i = (i+1)%KILL_BUFF_SIZE;
208     hi = kill_info.kb[i];
209     i = (i+1)%KILL_BUFF_SIZE;
210 
211     cnt = (hi * MAX_7_BIT_VAL) + lo;
212     kill_info.current_size = cnt;
213 
214     cnt -= TOTAL_SIZE_SPEC_BYTES;
215 
216     /* compare size of items to be yanked and previously yanked to  */
217     /* determine whether to expand or shrink the line being edited  */
218     if (cnt < kill_info.yank_size) {
219 
220         /* determine amount to shrink the line */
221         diff = kill_info.yank_size - cnt;
222 
223         /* shrink over the previous yanked item */
224         for (j = kill_info.yank_index+cnt; j < line->length-diff; j++) {
225             line->line[j] = line->line[j+diff];
226             line->size[j] = line->size[j+diff];    /* phx21233 R.L. - update display length flags */
227         }
228 
229         /* update line length to reflect shrinkage */
230         line->length -= diff;
231     }
232 
233     else if (kill_info.yank_size < cnt) {
234 
235         /* determine amount to expand */
236         diff = cnt - kill_info.yank_size;
237 
238         /* check and handle not enough room to contain yanked item */
239         if (line->length + diff >= MAX_LINE_SIZE) {
240             beep();
241             return;
242         }
243 
244         /* create gap at previously yanked item */
245         for (j = line->length; j >= kill_info.yank_index; j--) {
246             line->line[j+diff] = line->line[j];
247             line->size[j+diff] = line->size[j];    /* phx21233 R.L. - update display length */
248         }
249 
250         /* update the line length to reflect gap inserted */
251         line->length += diff;
252     }
253 
254     kill_info.yank_index = line->index;
255     kill_info.yank_size = cnt;    /* reset on save or RETURN */
256 
257     /* copy the item to be yanked over the previously yanked item */
258     for (j = 0; j < cnt; j++) {
259         line->line[line->index+j] = kill_info.kb[i];
260         line->size[line->index+j] = (kb.echo) ? TRUE : FALSE;  /* phx21233 R.L. - update display length */
261         i = (i+1)%KILL_BUFF_SIZE;
262     }
263 
264     /* update the screen and cursor */
265     redisplay(line,line->index,&(line->max_row),&(line->max_col));
266     cursor_right(line,kill_info.yank_size);
267 
268     /* update flag to indicate current item is not first item in    */
269     /* kill buffer                                                  */
270     kill_info.head_of_killbuff = FALSE;
271 }
272 
273 
274 
275 /*^L
276 **********************************************************************
277 
278   Routine:            SAVE_TO_KILLBUFF
279 
280   Function:
281       This routine saves an item (one or more characters) to the kill
282   buffer for subsequent retrieval via the yank() and yank_previous()
283   routines. The kill buffer is of fixed sized and when an item being
284   saved is larger than the space available in the kill buffer, then
285   room is created by freeing up previously stored items (from the
286   least recently saved to the most recently saved) until enough room
287   has been created.
288 
289   Parameters:
290      (input)          str - specifies the buffer containing the item
291                           to save
292      (input)          item_size - specifies the number of characters
293                           from the specified buffer to save
294 
295   Returns:            NONE
296 
297 **********************************************************************/
298 
299 save_to_killbuff(str,item_size)
300 char *str;
301 int item_size;
302 {
303     int i, j;
304     int lo_byte;
305     int hi_byte;
306     int first_chunk;
307     int buffer_empty;
308 
309     /* check for item too big to fit into killbuffer; this should */
310     /* not normally happen since the size of largest item is an */
311     /* input line that is completely full, and the size of the line */
312     /* buffer is currently less than the size of the kill buffer */
313     if (item_size+TOTAL_SIZE_SPEC_BYTES > KILL_BUFF_SIZE)
314 
315         /* truncate size of item so save so that it fits and continue */
316         item_size = KILL_BUFF_SIZE - TOTAL_SIZE_SPEC_BYTES;
317 
318 
319     /* delete items from tail until enough room */
320     while (kill_info.chars_free < item_size+TOTAL_SIZE_SPEC_BYTES) {
321 
322         i = (kill_info.tail + kill_info.tail_item_size) % KILL_BUFF_SIZE;
323 
324         /* if insertion will overwrite all existing items */
325         if (i == kill_info.head && kill_info.chars_free == NONE) {
326 
327             /* reinitialize everything */
328             init_killbuff();
329             break;
330         }
331 
332         /* determine size of least recently saved item */
333         kill_info.tail = i;
334         lo_byte = kill_info.kb[i];
335         i = (i+1)%KILL_BUFF_SIZE;
336         hi_byte = kill_info.kb[i];
337 
338         /* delete least recently saved item from kill buffer */
339         kill_info.chars_free += kill_info.tail_item_size;
340         kill_info.tail_item_size = (hi_byte * MAX_7_BIT_VAL) + lo_byte;
341     }
342 
343     /* set flag to determine whether kill buffer is now empty */
344     if (kill_info.head == kill_info.tail && kill_info.chars_free == KILL_BUFF_SIZE)
345         buffer_empty = TRUE;
346     else
347         buffer_empty = FALSE;
348 
349     /* determine space taken up in kill buffer for new item */
350     kill_info.head_item_size = item_size+TOTAL_SIZE_SPEC_BYTES;
351     kill_info.chars_free -= kill_info.head_item_size;
352 
353     /* size of item is to be stored in first two and last two       */
354     /* bytes of item in kill buffer; determine the values of the    */
355     /* size bytes                                                   */
356     lo_byte = kill_info.head_item_size % MAX_7_BIT_VAL;
357     hi_byte = kill_info.head_item_size / MAX_7_BIT_VAL;
358 
359     /* store into first two bytes of item */
360     i = kill_info.head;
361     kill_info.kb[i] = lo_byte;
362     i = (i+1)%KILL_BUFF_SIZE;
363     kill_info.kb[i] = hi_byte;
364     i = (i+1)%KILL_BUFF_SIZE;
365 
366     /* copy across end of buffer to beginning of buffer? */
367     if (i+item_size > KILL_BUFF_SIZE) {
368 
369         /* determine amount to copy up to end of buffer */
370         first_chunk = KILL_BUFF_SIZE - i;
371 
372         /* determine amount to copy starting from beginning of */
373         /* buffer */
374         item_size -= first_chunk;
375 
376         /* copy item to kill buffer */
377         for (j = 0; j < first_chunk; j++)
378             kill_info.kb[i++] = str[j];
379         for (i = 0; i < item_size; i++)
380             kill_info.kb[i] = str[j++];
381 
382     }
383 
384     /* no buffer wrap around, do straight copy */
385     else {
386         for (j = 0; j < item_size; j++)
387             kill_info.kb[i++] = str[j];
388     }
389 
390     /* last two bytes of item holds size of item in kill buffer */
391     i = i % KILL_BUFF_SIZE;
392     kill_info.kb[i] = lo_byte;
393     i = (i+1) % KILL_BUFF_SIZE;
394     kill_info.kb[i] = hi_byte;
395     i = (i+1) % KILL_BUFF_SIZE;
396 
397     /* if buffer was empty, make tail and head the same */
398     if (buffer_empty) {
399         kill_info.tail = kill_info.head;
400         kill_info.tail_item_size = kill_info.head_item_size;
401     }
402 
403     /* head index points to next location in kill buffer to add */
404     /* next item */
405     kill_info.head = i;
406 
407     /* set current item index to most currently saved item index */
408     kill_info.current = kill_info.head;
409 
410     /* current item size to currently saved item size */
411     kill_info.current_size = kill_info.head_item_size;
412 
413     /* index 'current' is now same as index 'head' */
414     kill_info.head_of_killbuff = TRUE;
415 
416     /* reset values used for yanking */
417     kill_info.yank_index = ZERO_INDEX_VALUE;
418     kill_info.yank_size = ZERO_BYTES;
419 
420 }
421 
422 
423 
424 /*^L
425 **********************************************************************
426 
427   Routine:            INIT_KILLBUFF
428 
429   Function:
430       This routine initializes the kill buffer to indicate an empty
431   kill buffer.
432 
433   Parameters:         NONE
434 
435   Returns:            NONE
436 
437 **********************************************************************/
438 
439 init_killbuff()
440 {
441 
442     /* all item indices set to 0 (beginning of kill buffer) and     */
443     /* all item sizes set to 0 (no items)                           */
444 
445     kill_info.head = ZERO_INDEX_VALUE;
446     kill_info.head_item_size = ZERO_BYTES;
447     kill_info.tail = ZERO_INDEX_VALUE;
448     kill_info.tail_item_size = ZERO_BYTES;
449     kill_info.current = ZERO_INDEX_VALUE;
450     kill_info.current_size = ZERO_BYTES;
451     kill_info.chars_free = KILL_BUFF_SIZE;
452 
453     /* 'current' is the same as 'head' */
454     kill_info.head_of_killbuff = TRUE;
455 
456     /* no items previously yanked */
457     kill_info.yank_index = ZERO_INDEX_VALUE;
458     kill_info.yank_size = ZERO_BYTES;
459 }
460