1 /* ********************************************
  2    *                                          *
  3    * Copyright, (C) Honeywell Bull Inc., 1988 *
  4    *                                          *
  5    ******************************************** */
  6 
  7 /* HISTORY COMMENTS:
  8   1) change(88-06-13,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-07-25,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
 12      install(88-10-12,MR12.2-1160):
 13      Documentation additions only. Added header comments to all routines.
 14   3) change(88-08-09,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
 15      install(88-10-12,MR12.2-1160):
 16      Fix references to include files; "wstdefs.h" was split into
 17      "wstdefs.h" and "wsttype.h" to avoid declaration clash; also,
 18      various constants defined to descriptive names.
 19                                                    END HISTORY COMMENTS */
 20 
 21 #include <dos.h>
 22 #include <ctype.h>
 23 #include "wstdefs.h"
 24 #include "wstglob.h"
 25 
 26 /*^L
 27 **********************************************************************
 28 
 29   Routine:            CURSOR_BOL
 30 
 31   Function:
 32       This routine moves the cursor to the beginning of the line.
 33   Since the entire input line may span several rows on the screen,
 34   this routine may be required to handle the case where the beginning
 35   of the line has scrolled off the top of the screen.
 36 
 37   Parameters:
 38      (input/output)   line - pointer to the structure containing the
 39                           line and information about the line being
 40                           edited in edit mode
 41 
 42   Returns:            NONE
 43 
 44 **********************************************************************/
 45 
 46 cursor_bol(line)
 47 EDIT_LINE *line;
 48 {
 49     int i;
 50 
 51     /* check that line is not empty */
 52     if (line->index > 0) {
 53 
 54         /* check if beginning of line displayed has scrolled */
 55         if (line->scrolled_flag) {
 56 
 57             /* need to redraw from beginning of display screen */
 58             set_cursor(ss.top,ss.left);
 59 
 60             /* blank pad to original display column */
 61             for (i = ss.left; i < line->orig_col; i++)
 62                 putch(' ');
 63 
 64             /* start coordinates now beginning of screen */
 65             line->orig_row = ss.top;
 66 
 67             /* no longer scrolled, redisplaying the beginning */
 68             line->scrolled_flag = FALSE;
 69 
 70             /* set current cursor coordinates */
 71             line->cur_row = ss.top;
 72             line->cur_col = line->orig_col;
 73 
 74             /* update the physical cursor location */
 75             set_cursor(line->cur_row,line->cur_col);
 76             line->index = 0;
 77 
 78             /* redisplay from beginning of line */
 79             redisplay(line,line->index,&(line->max_row),&(line->max_col));
 80         }
 81 
 82         /* no, beginning of line still on display */
 83         else {
 84 
 85             /* set keyboard buffer index to beginning of line */
 86             line->index = 0;
 87 
 88             /* move the physical cursor to the beginning of line on
 89                the display
 90             */
 91             line->cur_row = line->orig_row;
 92             line->cur_col = line->orig_col;
 93             set_cursor(line->cur_row,line->cur_col);
 94         }
 95     }
 96 }
 97 
 98 
 99 /*^L
100 **********************************************************************
101 
102   Routine:            CURSOR_EOL
103 
104   Function:
105       This routine moves the cursor to the end of the line in edit
106   mode. Since it is possible for the end of the line to be shifted
107   past the end of the screen due to inserting text, this routine has
108   to determine and handle scrolling in this case.
109 
110   Parameters:
111      (input/output)   line - pointer to the structure containing the
112                           line and information about the line being
113                           edited in edit mode
114 
115   Returns:            NONE
116 
117   Note 1 - Some care must be taken to maintain consistency in the
118       appearance of the screen when using this and other cursor
119       movement routines. Going to the end of the line using a series
120       of commands to move the cursor to the right or going directly
121       to the end of the line using this routine should yield the same
122       display of the line being edited.
123 **********************************************************************/
124 
125 cursor_eol(line)
126 EDIT_LINE *line;
127 {
128     int cnt;
129     int row;
130     int col;
131     int screen_width;
132     int screen_height;
133     int chars_to_pad;
134     int i;
135     char *ptr, *display_char_str();
136     int offset;
137     int lines_scrolled;
138 
139     /* determine screen width */
140     screen_width = ss.right - ss.left + 1;
141     screen_height = ss.bottom - ss.top + 1;
142 
143     /* get absolute row coordinate */
144     row = line->cur_row - ss.top;
145 
146     /* get absolute column coordinate */
147     col = line->cur_col - ss.left;
148     cnt = 0;
149 
150     /* determine number of character positions needed to
151        display from current cursor position (index) to end of line (in cnt);
152        keep track of where the cursor will end up when all text to end
153        of line is displayed (in row, col).
154     */
155     for (i = line->index; i < line->length; i++) {
156         cnt += line->size[i];
157         row += (col + line->size[i]) / screen_width;
158         col = (col + line->size[i]) % screen_width;
159     }
160 
161     /* if cursor would still be within screen boundary */
162     if (row < screen_height) {
163 
164         /* just move the cursor to the calculated position */
165         line->cur_row = row + ss.top;
166         line->cur_col = col + ss.left;
167         set_cursor(line->cur_row,line->cur_col);
168 
169         /* set current position (index) to end of line position */
170         line->index = line->length;
171         return;
172     }
173 
174     else {
175         lines_scrolled = row - screen_height + 1;
176 
177         /* We want to cursor to end up at the column position
178            calculated above (in col) but displayed at the bottom row of the
179            screen; determine number of character positions needed
180            to fill from the beginning of the screen
181         */
182         chars_to_pad = (screen_height - 1) * screen_width;
183         chars_to_pad += col;
184 
185         /* "cnt" contains the number of display locations
186            required to display from the end of the line to
187            the "i"th character
188         */
189         cnt = 0;
190         for (i = line->length-1; i >= 0; i--) {
191             cnt += line->size[i];
192             if (cnt >= chars_to_pad)
193                 break;
194         }
195 
196         /* if the character locations needed to display the
197            entire line is less than that needed to pad to the
198            beginning of the line, then line starts within the
199            screen display but part of the line extends beyond
200            what is displayed on the screen
201         */
202         if (i < 0 || cnt < chars_to_pad) {
203 
204             /* scroll enough lines for the last line to be displayed */
205             for (i = 0; i < lines_scrolled; i++)
206                 wst_scroll();
207 
208             /* calculate where the cursor should end up */
209             i = 0;
210             offset = ((screen_height - 1) * screen_width) + col;
211             offset -= cnt;
212             row = offset / screen_width;
213             col = offset % screen_width;
214 
215             row += ss.top;
216             col += ss.left;
217 
218             /* move the physical cursor to the calculated location */
219             set_cursor(row,col);
220 
221         }
222 
223         /* enough character to pad from the beginning of display to
224            end of line starting from the "i"th position
225         */
226         else {
227 
228             /* move physical cursor to start of display screen */
229             row = ss.top;
230             col = ss.left;
231             set_cursor(row,col);
232 
233             /* if total display locations starting from character the
234                "i"th position is more than that needed to pad to the
235                beginning of the screen, then only part of the "i"th
236                character displayed needs to be displayed at the beginning
237                of the display screen
238             */
239             if (cnt > chars_to_pad) {
240 
241                 /* get a pointer to the display string of that character */
242                 ptr = display_char_str(line->line[i]);
243 
244                 /* index into the portion of the display string
245                    needed to pad to fill the screen
246                 */
247                 ptr += strlen(ptr) - (cnt - chars_to_pad);
248 
249                 /* display that portion of the character string */
250                 while (*ptr) {
251                     putch(*ptr);
252                     ptr++;
253                     col++;
254                 }
255 
256                 /* start redisplay from next character */
257                 i++;
258             }
259         }
260 
261 
262         /* move keyboard buffer index to end of line */
263         line->index = line->length;
264 
265         /* initialize coordinates values to where to begin redisplay */
266         line->cur_row = row;
267         line->cur_col = col;
268 
269         /* do the redisplay */
270         redisplay(line,i,&(line->max_row),&(line->max_col));
271 
272         /* update the start and current cursor coordinates */
273         line->cur_row = line->max_row;
274         line->cur_col = line->max_col;
275 
276         /* check if screen has scrolled */
277         line->orig_row -= lines_scrolled;
278         if (line->orig_row < ss.top)
279             line->scrolled_flag = TRUE;
280 
281         /* update location of physical cursor */
282         set_cursor(line->cur_row,line->cur_col);
283     }
284 }
285 
286 
287 /*^L
288 **********************************************************************
289 
290   Routine:            CURSOR_LEFT
291 
292   Function:
293       This routine moves the physical and logical cursor to the
294   previous character in the edit mode line, if that character exists
295   (i.e. when not at the beginning of the line). Since it is possible
296   for part of the line to have scrolled off the top of the screen,
297   this routine has to detect and handle scrolling the line back down.
298 
299   Parameters:
300      (input/output)   line - pointer to the structure containing the
301                           line and information about the line being
302                           edited in edit mode
303      (input)          n_times - specifies the number of character
304                           locations to move the cursor backwards (to
305                           the left)
306 
307   Returns:            The actual number of characters that the cursor
308                           was moved back.
309 
310   Note 1 - If the number of characters from the current cursor
311       location to the beginning of the line is less than value
312       specified by 'n_times', the cursor is moved to the beginning of
313       the line. This condition may be checked by the caller by
314       checking if the value passed for 'n_times' is less than the
315       return value.
316 **********************************************************************/
317 
318 cursor_left(line,n_times)
319 EDIT_LINE *line;
320 int n_times;
321 {
322     int i;
323     int size;
324     int row_diff;
325     int col_diff;
326     int chars_to_bos;
327     int screen_width;
328     int chars_to_pad;
329     int new_index;
330     int pad;
331     int new_col;
332     char *ptr, *display_char_str();
333     int actual_times;
334 
335     /* don't do anything if at beginning of line or moving left 0 times */
336     if (line->index < 1 || n_times < 1)
337         return(0);
338 
339     /* limit the number of times to move to the number number of */
340     /* characters from current position to beginning of line */
341     if (n_times > line->index)
342         actual_times = line->index;
343     else
344         actual_times = n_times;
345 
346     /* determine the number of display characters to move left from the
347        current location and position the line index there
348     */
349     size = 0;
350     for (i = 0; i < actual_times; i++) {
351         line->index--;
352         size += line->size[line->index];
353     }
354 
355     /* determine the number of display characters from the current location
356        to the beginning of the display window
357     */
358     row_diff = line->cur_row - ss.top;
359     col_diff = line->cur_col - ss.left;
360     screen_width = ss.right - ss.left + 1;
361     chars_to_bos = (row_diff * screen_width) + col_diff;
362 
363     /* handle case for moving left beyond display window origin:
364            redisplay() will automatically update to the right of
365            the cursor if the current coordinates in line->cur_row
366            and line->cur_col are set and the index into the actual
367            line buffer is given. The cursor will be on the first
368            row of the display window. We need to calculate the column
369            position based on the number of times moved left. Also,
370            we need to fill the beginning of the first line up to the
371            calculated column position since redisplay() won't do it.
372     */
373 
374     if (chars_to_bos < size) {
375 
376         /* need to scroll back, so redraw always start at window origin */
377 
378         line->cur_row = ss.top;
379         line->cur_col = ss.left;
380         set_cursor(line->cur_row,line->cur_col);
381 
382         /* determine which column on the starting row that cursor
383            should be positioned at
384         */
385 
386         chars_to_pad = screen_width -
387            ((size - chars_to_bos) % screen_width);
388 
389         /* handle wrap around case */
390         if (chars_to_pad == screen_width) chars_to_pad = 0;
391 
392         /* calculate absolute screen coordinates */
393         new_col = ss.left + chars_to_pad;
394 
395         /* check if updating to the left of cursor is necessary */
396         if (chars_to_pad > 0) {
397 
398             new_index = line->index - 1;
399 
400             pad = 0;
401             for (i = new_index; i >= 0; i--) {
402                 pad += line->size[i];
403                 if (pad >= chars_to_pad)
404                     break;
405             }
406 
407             /* if not enough characters to pad to beginning of line, then
408                line must have started somewhere in the middle of the display
409                window, just blank fill the beginning of the line and then
410                redisplay
411             */
412             if (i < 0) {
413 
414                 for (i = 0; i < chars_to_pad - pad; i++) {
415                     line->cur_col++;
416                     putch(' ');
417                 }
418                 redisplay(line,0,&(line->max_row),&(line->max_col));
419 
420                 /* set cursor to the column position determined */
421                 line->cur_col = new_col;
422                 set_cursor(line->cur_row,line->cur_col);
423 
424                 return(actual_times);
425             }
426 
427             /* a single character may need 2 or more characters to display
428                the character, e.g. 10 blanks for a tab or \ 0 0 0
429                for a NUL. Check if line starts in the middle of
430                one of these and if so, print out the portion needed to
431                pad the beginning of the line
432             */
433             if (pad > chars_to_pad) {
434 
435                 /* get the string representing the current character */
436                 ptr = display_char_str(line->line[i]);
437 
438                 /* index into the string the portion needed to pad the line */
439                 ptr += (strlen(ptr) - (pad - chars_to_pad));
440 
441                 /* display this portion of the string */
442                 while (*ptr) {
443                     putch(*ptr);
444                     ptr++;
445                     line->cur_col++;
446                 }
447                 /* start redisplaying at the next character in buffer */
448                 i++;
449             }
450         }
451 
452         /* no padding at beginning of line necessary, redisplay at current
453            index position */
454         else
455             i = line->index;
456 
457         redisplay(line,i,&(line->max_row),&(line->max_col));
458 
459         /* position the cursor to the calculated location */
460         line->cur_row = ss.top;
461         line->cur_col = new_col;
462         set_cursor(line->cur_row,line->cur_col);
463 
464         return(actual_times);
465 
466     }
467 
468     /* final location is still on the screen, back up and reposition there */
469     else {
470         for (i = 0; i < size; i++) {
471             line->cur_col--;
472             if (line->cur_col < ss.left) {
473                 line->cur_col = ss.right;
474                 line->cur_row--;
475             }
476         }
477         set_cursor(line->cur_row,line->cur_col);
478         return(actual_times);
479     }
480 }
481 
482 
483 /*^L
484 **********************************************************************
485 
486   Routine:            CURSOR_RIGHT
487 
488   Function:
489       This routine moves the physical and logical cursor one
490   character forward (to the right). The routine detects and handles
491   the case of scrolling the screen when the end of the screen is
492   reached.
493 
494   Parameters:
495      (input/output)   line - specifies the structure containing the
496                           line and information about the line being
497                           edited in edit mode
498      (input)          n_times - specifies the number of times to the
499                           cursor a character position to the right
500 
501   Returns:            The actual number of times the cursor was moved
502                           forward (to the right)
503 
504   Note 1 - If the number of characters from the current cursor
505       location to the end of the line is less than the value
506       specified by 'n_times', the cursor is moved to the end of the
507       line. This condition may be checked by the caller by checking
508       if the value passed for 'n_times' is less than the return
509       value.
510 **********************************************************************/
511 
512 cursor_right(line,n_times)
513 EDIT_LINE *line;
514 int n_times;
515 {
516     int i;
517     int size;
518     int row_diff;
519     int col_diff;
520     int chars_to_eos;
521     CURPOS tpos;
522     int actual_times;
523     int chars_to_eol;
524 
525     /* check that we're not already at the end of the line */
526     if (line->index >= line->length)
527         return(0);
528 
529     /* determine the number of characters from current cursor position to
530        the end of the line
531     */
532     chars_to_eol = line->length - line->index;
533 
534     /* if number of times to move cursor right exceeds number of
535        characters to the right of the cursor, set the actual number
536        of times to move to the number of characters to the right of cursor
537     */
538     if (chars_to_eol > n_times)
539         actual_times = n_times;
540     else
541         actual_times = chars_to_eol;
542 
543     /* calculate the display columns to move to the right (a character
544        may require more than one display column, e.g. tab may require 10)
545     */
546     size = 0;
547     for (i = 0; i < actual_times; i++)
548         size += line->size[line->index+i];
549 
550     /* calculate the number of display columns to end of screen */
551     row_diff = ss.bottom - line->cur_row;
552     col_diff = ss.right - line->cur_col;
553     chars_to_eos = (row_diff * (ss.right - ss.left + 1)) + col_diff;
554 
555     /* determine if new cursor location passes end of screen */
556     if (chars_to_eos >= size) {
557 
558         /* no, just move calculate where the cursor will end up */
559         for (i = 0; i < size; i++) {
560             line->cur_col++;
561             if (line->cur_col > ss.right) {
562                 line->cur_col = ss.left;
563                 line->cur_row++;
564             }
565         }
566 
567         /* move the physical cursor there */
568         set_cursor(line->cur_row,line->cur_col);
569 
570         /* move keyboard buffer index to the right */
571         line->index += actual_times;
572     }
573 
574     /* new cursor location passes end of screen */
575     else {
576 
577         /* initialize structure for keeping track of cursor */
578         init_temp_curpos(&tpos,line);
579 
580         /* display each character (scrolling if necessary)
581            until the specified number of characters to the right
582            has been displayed
583         */
584         for (i = 0; i < actual_times; i++) {
585             display_char(&tpos,line->line[line->index]);
586             line->index++;
587         }
588 
589         /* update current cursor coordinate values */
590         line->cur_row = tpos.cur_row;
591         line->cur_col = tpos.cur_col;
592         line->orig_row -= tpos.scroll_flag;
593 
594         if (line->orig_row < ss.top)
595             line->scrolled_flag = TRUE;
596 
597         /* redisplay rest of line to end of screen */
598         redisplay(line,line->index,&(line->max_row),&(line->max_col));
599     }
600 
601     /* return the actual amount moved to the right */
602     return(actual_times);
603 }
604 
605 
606 
607 /*^L
608 **********************************************************************
609 
610   Routine:            FORWARD_WORD
611 
612   Function:
613       This routine moves the physical and logical cursor forward
614   until the end of a word is reached. The number of times this
615   operation is repeated may be specified.
616 
617   Parameters:
618      (input/output)   line - pointer to the structure containing the
619                           line and information about the line being
620                           edited in edit mode
621      (input)          n_times - specifies the number of times to move
622                           the cursor forward a word
623 
624   Returns:            NONE
625 
626 **********************************************************************/
627 
628 forward_word(line,n_times)
629 EDIT_LINE *line;
630 int n_times;
631 {
632     int i;
633     int tmp_index;
634     int loop_cnt;
635 
636     /* do nothing if already at end of line */
637     if (line->index >= line->length) return;
638 
639     /* get copy of keyboard buffer index */
640     i = line->index;
641 
642     loop_cnt = 0;
643 
644     /* perform the specified number of times or until end of line reached */
645     while (loop_cnt < n_times && i < line->length) {
646 
647         /* save index in case no words skipped */
648         tmp_index = i;
649 
650         /* skip any word delimiters */
651         while (i < line->length && is_word_delim(line->line[i]))
652             i++;
653 
654         /* no next word, do nothing */
655         if (i >= line->length) {
656             /* restore to position before word delimiters skipped */
657             i = tmp_index;
658             break;
659         }
660 
661         /* skip any characters until word delimiter reached */
662         while (i < line->length && !is_word_delim(line->line[i]))
663             i++;
664 
665         /* tally the word skipped */
666         loop_cnt++;
667 
668     }
669 
670     /* move and update cursor only if moved forward at least 1 word */
671     if (loop_cnt > 0) {
672         /* if keyboard buffer did not change, just return */
673         if (i == line->index)
674             return;
675 
676         /* move the keyboard right the calculated number of times */
677         cursor_right(line,i-line->index);
678     }
679 }
680 
681 
682 
683 /*^L
684 **********************************************************************
685 
686   Routine:            BACKWARD_WORD
687 
688   Function:
689       This routine moves the physical and logical cursor to the
690   beginning of the previous word. The number of times this operation
691   is repeated may be specified.
692 
693   Parameters:
694      (input/output)   line - pointer to the structure containing the
695                           line and information about the line being
696                           edited in edit mode
697      (input)          n_times - specifies the number of times to move
698                           the cursor backward a word
699 
700   Returns:            NONE
701 
702 **********************************************************************/
703 
704 backward_word(line,n_times)
705 EDIT_LINE *line;
706 int n_times;
707 {
708     int i;
709     int tmp_index;
710     int loop_cnt;
711 
712     /* do nothing if already at beginning of line */
713     if (line->index < 1) return;
714 
715     /* assume starting at one character before cursor */
716     i = line->index - 1;
717 
718     loop_cnt = 0;
719    /* move specified number of words back or until beginning of line reached */
720     while (loop_cnt < n_times && i >= 0) {
721 
722         /* save index in case no words skipped */
723         tmp_index = i;
724 
725         /* skip over any word delimiters */
726         while (i >= 0 && is_word_delim(line->line[i]))
727             i--;
728 
729         /* do nothing if no previous word */
730         if (i < 0) {
731             /* don't move cursor position if just word delimiters */
732             i = tmp_index;
733             break;
734         }
735 
736         /* skip until a word delimiter is reached */
737         while (i >= 0 && !is_word_delim(line->line[i]))
738             i--;
739 
740         /* tally the word skipped */
741         loop_cnt++;
742     }
743 
744     /* move and update cursor only if moved backwards least one word */
745     if (loop_cnt > 0) {
746         /* restore index position from delimiter reached (or past beginning
747            of line) to beginning of word (or start of line)
748         */
749         i++;
750 
751         /* move the cursor left the calculated number of times */
752         cursor_left(line,line->index-i);
753     }
754 }
755 
756 
757 
758 /*^L
759 **********************************************************************
760 
761   Routine:            IS_WORD_DELIM
762 
763   Function:
764       This function, given an ASCII character, specifies whether that
765   character is a word delimiter or not. This function is used by the
766   forward_word and backward_word routines.
767 
768   Parameters:
769      (input)          ch - specifies the ASCII character being
770                           checked as a word delimiter character
771 
772   Returns:            TRUE if the specifies character is a word
773                           delimiter
774                       FALSE if the character is NOT a word delimiter
775 
776 **********************************************************************/
777 
778 is_word_delim(ch)
779 int ch;
780 {
781     /* if valid ASCII character and is a space character, then word delimter */
782     if (ch < ASCII_DEL && isspace(ch)) return(TRUE);
783 
784     /* check for special punctuations that are not word delimiters */
785     if (ch < ASCII_DEL && ispunct(ch)) {
786         if (ch == '_' || ch == '$' || ch == '-')
787             return(FALSE);
788         return(TRUE);
789     }
790     return(FALSE);
791 }
792 
793 
794 
795 /*^L
796 **********************************************************************
797 
798   Routine:            BACKUP_CURSOR
799 
800   Function:
801       This routine just moves the physical cursor backwards a
802   specified number of character positions on the screen and updates
803   the cursor position fields in the structure containing the line and
804   information about the line being edited in edit mode.
805 
806   Parameters:
807      (input/output)   line - pointer to the structure containing the
808                           line and information about the line being
809                           edited in edit mode
810      (input)          n_times - specifies the number of times to move
811                           the cursor backwards a character
812 
813   Returns:            NONE
814 
815 **********************************************************************/
816 
817 backup_cursor(line,n_times)
818 EDIT_LINE *line;
819 int n_times;
820 {
821     int i;
822 
823     /* perform the specified number of times */
824     for (i = 0; i < n_times; i++) {
825 
826         /* decrement cursor column value */
827         line->cur_col--;
828 
829         /* past left edge? */
830         if (line->cur_col < ss.left) {
831 
832             /* go to end of previous line */
833             line->cur_col = ss.right;
834             line->cur_row--;
835         }
836     }
837 
838     /* update the physical cursor location */
839     set_cursor(line->cur_row,line->cur_col);
840 }