1 /*
2  * YASR ("Yet Another Screen Reader") is an attempt at a lightweight,
3  * portable screen reader.
4  *
5  * Copyright (C) 2001-2003 by Michael P. Gorse. All rights reserved.
6  *
7  * YASR comes with ABSOLUTELY NO WARRANTY.
8  *
9  * This is free software, placed under the terms of the
10  * GNU Lesser General Public License, as published by the Free Software
11  * Foundation.  Please see the file COPYING for details.
12  *
13  * Web Page: http://yasr.sf.net
14  *
15  * This software is maintained by:
16  * Michael P. Gorse <mgorse@users.sourceforge.net>
17  */
18 
19 #include "yasr.h"
20 
21 static void ui_saypos(int, int);
22 
ui_funcman(int (* f)(int))23 void ui_funcman(int (*f) (int))
24 {
25   static void *fstack[8];	/* crappy stack, but who cares. */
26   static int nf = 0;
27 
28   if (f)
29   {
30     fstack[nf++] = (void *) ui.func;
31     ui.func = f;
32     (*ui.func) (0);
33   } else
34   {
35     ui.func = (int (*)(int)) fstack[--nf];
36   }
37 }
38 
39 
rev_rttc(int * argp)40  /*ARGSUSED*/ void rev_rttc(int *argp)
41 {
42   int cr, cc;
43   int i;
44 
45   if (ui.revmode)
46   {
47     cr = rev.cr;
48     cc = rev.cc;
49   } else
50   {
51     cr = win->cr;
52     cc = win->cc;
53   }
54   for (i = 0; i < cr; i++)
55   {
56     ui_sayline(i, 0);
57   }
58   ui_saylinepart(i, 0, cc, 0);
59 }
60 
61 
rev_rctb(int * argp)62  /*ARGSUSED*/ void rev_rctb(int *argp)
63 {
64   int i;
65   int cr, cc;
66 
67   if (ui.revmode)
68   {
69     cr = rev.cr;
70     cc = rev.cc;
71   } else
72   {
73     cr = win->cr;
74     cc = win->cc;
75   }
76   ui_saylinepart(cr, cc, -1, 0);
77   for (i = cr + 1; i < win->rows; i++)
78   {
79     ui_sayline(i, 0);
80   }
81 }
82 
83 
rev_rs(int * argp)84 void rev_rs(int *argp)
85 {
86   int i;
87   int a, b;
88 
89   if (argp)
90   {
91     a = *argp;
92     b = *(argp + 1);
93   } else
94   {
95     a = 0;
96     b = win->rows - 1;
97   }
98 
99   for (i = a; i <= b; i++)
100   {
101     ui_sayline(i, 0);
102   }
103 }
104 
105 
rev_curpos(int * argp)106  /*ARGSUSED*/ void rev_curpos(int *argp)
107 {
108   ui_saypos(rev.cr, rev.cc);
109 }
110 
111 
ui_sayword(int cr,int cc)112 void ui_sayword(int cr, int cc)
113 {
114   chartype *c;
115   int cp;
116   int i = 0;
117 
118   c = win->row[cr] + cc;
119   cp = cc;
120   while (cp && (*c & 0xff) > 32)
121   {
122     c--;
123     cp--;
124   }
125   if ((*c & 0xff) <= 32)
126   {
127     c++;
128     cp++;
129   }
130   while (cp < win->cols && (*c & 0xff) > 32)
131   {
132     cp++;
133     buf[i++] = realchar(*c++);
134   }
135   if (i)
136   {
137     speak((char *) buf, i);
138   }
139 }
140 
141 
rev_line(int * argp)142 void rev_line(int *argp)
143 {
144   int nr, nc;
145 
146   if (ui.revmode)
147   {
148     nr = rev.cr;
149     nc = rev.cc;
150   } else
151   {
152     nr = win->cr;
153     nc = win->cc;
154   }
155   if (argp && (*argp & 0xff00) == 0x0100)
156   {
157     nr = *argp & 0xff;
158   } else if (argp)
159   {
160     nr += (*argp * (ui.num ? ui.num : 1));
161   } else if (ui.num)
162   {
163     nr = ui.num - 1;
164   }
165 
166   if (nr < 0)
167   {
168     nr = 0;
169     tts_say(_("top"));
170   }
171   else if (nr >= win->rows)
172   {
173     nr = win->rows - 1;
174     tts_say(_("bottom"));
175   }
176   if (ui.revmode)
177   {
178     rev.cr = nr;
179   }
180 
181   if (!argp || !rev.udmode || !(*(argp + 1)))
182   {
183     ui_sayline(nr, 1);
184     return;
185   }
186 
187   switch (rev.udmode)
188   {
189   case 1:
190     ui_saychar(nr, nc);
191     break;
192   case 2:
193     ui_sayword(nr, nc);
194     break;
195   }
196 }
197 
198 
rev_word(int * argp)199 void rev_word(int *argp)
200 {
201   int nw;
202   int nr, nc;
203 
204   if (ui.revmode)
205   {
206     nr = rev.cr;
207     nc = rev.cc;
208   } else
209   {
210     nr = win->cr;
211     nc = win->cc;
212   }
213   nw = argp ? *argp : 0;
214 
215   if (nw > 0)
216   {
217     while (!cblank(nr, nc) && nc < win->cols)
218     {
219       nc++;
220     }
221   }
222   if (nw < 0)
223   {
224     while (nc && !cblank(nr, nc))
225     {
226       nc--;
227     }
228   }
229 
230   while (nw > 0)
231   {
232     nw--;
233     for (;;)
234     {
235       if (++nc >= win->cols)
236       {
237 	if (++nr == win->rows)
238 	{
239 	  tts_say(_("bottom right"));
240 	  nr--;
241 	  nc--;
242 	  break;
243 	}
244 	nc = 0;
245       }
246       if (!cblank(nr, nc))
247 	break;
248     }
249   }
250 
251   while (nw < 0)
252   {
253     nw++;
254     for (;;)
255     {
256       if (--nc < 0)
257       {
258 	if (--nr < 0)
259 	{
260 	  tts_say(_("top left"));
261 	  nr = nc = 0;
262 	  break;
263 	}
264 	nc = win->cols - 1;
265       }
266       if (!cblank(nr, nc))
267       {
268 	while (nc && !cblank(nr, nc - 1))
269 	{
270 	  nc--;
271 	}
272 	break;
273       }
274     }
275   }
276   ui_sayword(nr, nc);
277   if (ui.revmode)
278   {
279     rev.cr = nr;
280     rev.cc = nc;
281   }
282 }
283 
284 
ui_sayphonetic(int row,int col)285 static void ui_sayphonetic(int row, int col)
286 {
287   tts_sayphonetic((char) win->row[row][col]);
288 }
289 
290 
rev_ch(int * argp)291 void rev_ch(int *argp)
292 {
293   int nr, nc;
294 
295   if (ui.revmode)
296   {
297     nr = rev.cr;
298     nc = rev.cc;
299   } else
300   {
301     nr = win->cr;
302     nc = win->cc;
303   }
304 
305   if (argp)
306   {
307     nc += (*argp * (ui.num ? ui.num : 1));
308   } else if (ui.num)
309   {
310     nc = ui.num - 1;
311   }
312   while (nc < 0)
313   {
314     nr--;
315     nc += win->cols;
316   }
317   while (nc >= win->cols)
318   {
319     nr++;
320     nc -= win->cols;
321   }
322   if (nr < 0)
323   {
324     nr = nc = 0;
325     tts_say(_("top left"));
326   }
327   else if (nr >= win->rows)
328   {
329     nr = win->rows - 1;
330     nc = win->cols - 1;
331     tts_say(_("bottom right"));
332   }
333   (rev.repeat & 1 && !argp ? ui_sayphonetic : ui_saychar) (nr, nc);
334   if (ui.revmode)
335   {
336     rev.cr = nr;
337     rev.cc = nc;
338   }
339 }
340 
341 
ui_ennum(int ch)342 int ui_ennum(int ch)
343 {
344   if (!ch)
345   {
346     ui.num = ui.abort = 0;
347     return (1);
348   } else if (ch < 0x0100 && isdigit(ch))
349   {
350     ui.num = ui.num * 10 + (ch - 48);
351     return (1);
352   }
353   ui_funcman(0);
354   if (ch == 27)
355   {
356     tts_say(_("Aborting."));
357     ui.abort = 1;
358     ui.num = 0;
359   }
360 
361   return (2);
362 }
363 
ui_build_str(int ch)364 int ui_build_str(int ch)
365 {
366   switch (ch)
367   {
368   case 0:
369     ui.strlen = ui.abort = 0;
370     return 1;
371   case 0x08:
372   case 0x7f:
373     if (ui.strlen)
374     {
375       tts_saychar(ui.str[--ui.strlen]);
376     }
377     return 1;
378   case 27:	/* escape */
379     tts_say(_("Aborting."));
380     ui.abort = 1;
381     ui_funcman(0);
382     return 2;
383   case 13:
384   case 10:
385     ui.str[ui.strlen] = 0;
386     ui_funcman(0);
387     return 2;
388   default:
389     if (ui.strlen < sizeof(ui.str) - 1)	/* ascii dep. */
390     {
391       ui.str[ui.strlen++] = ch;
392     }
393     return 1;
394   }
395 }
396 
397 
rev_searchline(int l,int c1,int c2,int reverse)398 static int rev_searchline(int l, int c1, int c2, int reverse)
399 {
400   int i, j, len;
401   int a, b, step;
402   chartype *cp;
403 
404   len = strlen(rev.findbuf);
405   if (c2 == -1)
406   {
407     c2 = win->cols - len;
408   }
409   if (c2 < c1)
410   {
411     return (0);
412   }
413   if (reverse)
414   {
415     a = c2;
416     b = c1 - 1;
417     step = -1;
418   } else
419   {
420     a = c1;
421     b = c2 + 1;
422     step = 1;
423   }
424   for (i = a, cp = win->row[l] + a; i != b; i += step, cp += step)
425   {
426     for (j = 0; j < len; j++)
427     {
428       if (i + j < win->cols &&
429 	  toupper(realchar(*(cp + j))) != toupper(rev.findbuf[j]))
430       {
431 	break;
432       }
433     }
434     if (j == len)
435     {
436       rev.cr = l;
437       rev.cc = i;
438       ui_saypos(rev.cr, rev.cc);
439       return (1);
440     }
441   }
442 
443   return (0);
444 }
445 
446 
rev_searchtocursor(int * argp)447  /*ARGSUSED*/ void rev_searchtocursor(int *argp)
448 {
449   int i;
450 
451   if (rev.cc && rev_searchline(rev.cr, 0, rev.cc - 1, 1))
452   {
453     return;
454   }
455   for (i = rev.cr - 1; i >= 0; i--)
456   {
457     if (rev_searchline(i, 0, -1, 1))
458     {
459       return;
460     }
461   }
462   tts_say(_("not found"));
463 }
464 
rev_searchtoend(int * argp)465  /*ARGSUSED*/ void rev_searchtoend(int *argp)
466 {
467   int i;
468 
469   if (rev_searchline(rev.cr, rev.cc + 1, -1, 0))
470   {
471     return;
472   }
473   for (i = rev.cr + 1; i < win->rows; i++)
474   {
475     if (rev_searchline(i, 0, -1, 0))
476     {
477       return;
478     }
479   }
480   tts_say(_("not found"));
481 }
482 
483 
rev_searchall(int * argp)484  /*ARGSUSED*/ static void rev_searchall(int *argp)
485 {
486   int i;
487 
488   for (i = 0; i < win->rows; i++)
489   {
490     if (rev_searchline(i, 0, -1, 0))
491     {
492       return;
493     }
494   }
495   tts_say(_("not found"));
496 }
497 
498 
rev_find_aux(int ch)499 static int rev_find_aux(int ch)
500 {
501   if (!rev.meta)
502   {
503     switch (ch)
504     {
505     case 0:			/* initialize */
506       rev.findbuflen = rev.meta = 0;
507       tts_say(_("Enter pattern to find"));
508       return (1);
509 
510     case '>':
511       rev.findbuf[rev.findbuflen] = 0;
512       ui_funcman(0);
513       rev_searchtoend(NULL);
514       return (1);
515 
516     case '<':
517       rev.findbuf[rev.findbuflen] = 0;
518       ui_funcman(0);
519       rev_searchtocursor(NULL);
520       return (1);
521 
522     case '\\':
523       rev.meta = 1;
524       return (1);
525 
526     case 0x08:
527     case 0x7f:
528       if (rev.findbuflen)
529       {
530 	tts_saychar(rev.findbuf[--rev.findbuflen]);
531       }
532       return (1);
533 
534     case 27:			/* escape */
535       tts_say(_("Aborting."));
536       ui_funcman(0);
537       ui.abort = 1;
538       return 1;
539 
540     case 13:
541     case 10:
542       rev.findbuf[rev.findbuflen] = 0;
543       ui_funcman(0);
544       rev_searchall(NULL);
545       return (1);
546     }
547   }
548   rev.meta = 0;
549   if (rev.findbuflen >= win->cols)
550   {
551     return (1);
552   }
553   rev.findbuf[rev.findbuflen++] = (char) ch;
554 
555   return (1);
556 }
557 
558 
rev_find(int * argp)559  /*ARGSUSED*/ void rev_find(int *argp)
560 {
561   ui_funcman(&rev_find_aux);
562 }
563 
564 
rev_toline(int * argp)565 void rev_toline(int *argp)
566 {
567   int arg1 = *argp, arg2 = *(argp + 1), reading = 0;
568 
569   if (arg1 < 0)
570   {
571     if (arg1 <= -win->rows)
572     {
573       arg1 = -win->rows + 1;
574     }
575     arg1 = win->rows + arg1;
576   } else
577   {
578     if (!arg1 || arg1 > win->rows)
579     {
580       arg1 = win->rows;
581     }
582   }
583 
584   rev.cr = arg1 - 1;
585   if (!arg2)
586   {
587     return;
588   }
589 
590   if (arg2 == 1 || arg2 == 3)
591   {
592     if (!rev.cr)
593     {
594       tts_say(_("top"));
595     }
596     else if (rev.cr == win->rows - 1)
597     {
598       tts_say(_("bottom"));
599     }
600   }
601 
602   if (arg2 > 1)
603   {
604     reading = 1;
605   }
606   ui_sayline(rev.cr, reading);
607 }
608 
609 
rev_tocol(int * argp)610 void rev_tocol(int *argp)
611 {
612   int arg1 = *argp, arg2 = *(argp + 1), i, c = -1;
613 
614   if (arg1 > 0xff)
615   {
616     arg1 -= 256;
617     c = 0;
618     for (i = 0; i <= win->cols; i++)
619     {
620       if (win->row[rev.cr][i] > ' ')
621       {
622 	c = i;
623       }
624     }
625   }
626   if (arg1 < 0)
627   {
628     if (arg1 <= -win->cols)
629     {
630       arg1 = -win->cols + 1;
631     }
632     arg1 = win->cols + arg1;
633   } else if (!arg1 || arg1 > win->cols)
634   {
635     arg1 = win->cols;
636   }
637 
638   if (c > 0 && arg1 > c)
639   {
640     rev.cc = c;
641   } else
642   {
643     rev.cc = arg1 - 1;
644   }
645   if (!arg2)
646   {
647     return;
648   }
649 
650   if (arg2 >= 2)
651   {
652     if (!rev.cc)
653     {
654       tts_say(_("left"));
655     }
656     else if (rev.cc == win->cols - 1 || rev.cc == c)
657     {
658       tts_say(_("right"));
659     }
660   }
661   ui_saychar(rev.cr, rev.cc);
662 }
663 
664 
rev_main(int ch)665 static int rev_main(int ch)
666 {
667   int index;
668   Keybind *kf;
669 
670   if (ch < 0x100 && isdigit(ch))
671   {
672     ui_funcman(&ui_ennum);
673     return (2);
674   }
675   index = kb_search(&rev.keymap, ch);
676   if (index == -1)
677   {				/* invalid key */
678     rev.used = 0;
679     return (0);
680   }
681   kf = &rev.keymap.kb[index];
682   while (kf)
683   {
684     (*funcs[kf->index].f) (kf->argp);
685     kf = kf->next;
686   }
687   ui.num = 0;
688 
689   return (1);
690 }
691 
line_is_blank(int row,int c1,int c2)692 static int line_is_blank(int row, int c1, int c2)
693 {
694   int i;
695   int len;
696   chartype *rptr;
697 
698   len = c2 - c1;
699   rptr = win->row[row] + c1;
700   for (i = 0; i < len; i++)
701   {
702     if (!y_isblank(*rptr++))
703       return 0;
704   }
705   return 1;
706 }
707 
rev_nextpar(int * argp)708 void rev_nextpar(int *argp)
709 {
710   int i;
711 
712   for (i = rev.cr; i < win->rows && !line_is_blank(i, 0, win->cols); i++);
713   for (; i < win->rows && line_is_blank(i, 0, win->cols); i++);
714   if (i == win->rows)
715     return;
716   rev.cr = i;
717   rev.cc = 0;
718   rev_rctb(NULL);
719 }
720 
rev_prevpar(int * argp)721 void rev_prevpar(int *argp)
722 {
723   int i;
724 
725   if (rev.cr == 0)
726     return;
727   for (i = rev.cr; i >= 0 && !line_is_blank(i, 0, win->cols); i--);
728   if (i >= rev.cr - 1)
729   {
730     for (; i >= 0 && line_is_blank(i, 0, win->cols); i--);
731     for (; i >= 0 && !line_is_blank(i, 0, win->cols); i--);
732     if (i < 0)
733     {
734       for (i = 0; i < win->rows && line_is_blank(i, 0, win->cols); i++);
735       if (i >= rev.cr)
736 	return;
737     } else
738       i++;
739   } else
740     i++;
741   rev.cr = i;
742   rev.cc = 0;
743   rev_rctb(NULL);
744 }
745 
ui_bypass(int * argp)746  /*ARGSUSED*/ void ui_bypass(int *argp)
747 {
748   ui.meta = 1;
749 }
750 
751 
ui_revtog(int * argp)752  /*ARGSUSED*/ void ui_revtog(int *argp)
753 {
754   ui.revmode ^= 1;
755   if (ui.revmode == 0)
756   {
757     tts_say(_("exit"));
758     ui.func = NULL;
759     return;
760   }
761   tts_say(_("review"));
762   ui.func = &rev_main;
763   rev.findbuflen = rev.meta = ui.num = 0;
764   if (!ui.rc_detached)
765   {
766     rev.cr = win->cr;
767     rev.cc = win->cc;
768   }
769 }
770 
ui_detachtog(int * argp)771  /*ARGSUSED*/ void ui_detachtog(int *argp)
772 {
773   ui.rc_detached ^= 1;
774   if (ui.rc_detached)
775   {
776     tts_say(_("Detached"));
777     return;
778   }
779   rev.cr = win->cr;
780   rev.cc = win->cc;
781   tts_say(_("Attached"));
782 }
783 
ui_routerc(int * argp)784 void ui_routerc(int *argp)
785 {
786   rev.cr = win->cr;
787   rev.cc = win->cc;
788   if (*argp)
789   {
790     if (*argp > 1)
791     {
792       tts_say(_("Cursor routed."));
793     }
794     if (*argp == 1 || *argp == 3)
795     {
796       ui_saypos(rev.cr, rev.cc);
797     }
798   }
799 }
800 
801 /* Returns non-zero if the key has been processed. */
ui_keypress(int key)802 int ui_keypress(int key)
803 {
804   Keybind *kf;
805   int used = 0;
806   int index;
807 
808   if (ui.func)
809   {
810     while ((used = (*ui.func) (key)) == 2)
811     {
812       continue;
813     }
814   }
815   if (used)
816   {
817     return (used);
818   }
819   index = kb_search(&ui.keymap, key);
820   if (index == -1)
821   {
822     if (!ui.revmode && kbuflen < 99)
823       kbuf[kbuflen++] = key;
824     return (ui.revmode);
825   }
826   kf = &ui.keymap.kb[index];
827   if (key == rev.lastkey)
828   {
829     rev.repeat++;
830   } else
831   {
832     rev.repeat = 0;
833   }
834   while (kf)
835   {
836     (*funcs[kf->index].f) (kf->argp);
837     kf = kf->next;
838   }
839   ui.num = 0;
840   rev.lastkey = key;
841 
842   return (1);
843 }
844 
845 
ui_saychar(int row,int col)846 void ui_saychar(int row, int col)
847 {
848   tts_saychar((char) win->row[row][col]);
849 }
850 
851 
ui_sayblankline()852 static void ui_sayblankline()
853 {
854   tts_say(_("blank"));
855 }
856 
ui_saylinepart(int row,int c1,int c2,int say_blank)857 void ui_saylinepart(int row, int c1, int c2, int say_blank)
858 {
859   int blank = 1;
860   int len;
861   chartype *rptr;
862   int i;
863 
864   if (c2 == -1)
865   {
866     c2 = win->cols;
867   }
868   len = c2 - c1;
869   rptr = win->row[row] + c1;
870   for (i = 0; i < len; i++)
871   {
872     buf[i] = realchar(*(rptr++));
873     if (buf[i] != 32)
874       blank = 0;
875   }
876   if (blank)
877   {
878     if (say_blank)
879     {
880       ui_sayblankline();
881     }
882     return;
883   }
884   speak((char *) buf, len);
885 }
886 
ui_sayline(int row,int say_blank)887 void ui_sayline(int row, int say_blank)
888 {
889   ui_saylinepart(row, 0, win->cols, say_blank);
890 }
891 
ui_curpos(int * argp)892  /*ARGSUSED*/ void ui_curpos(int *argp)
893 {
894   ui_saypos(win->cr, win->cc);
895 }
896 
897 
ui_silence(int * argp)898 void ui_silence(int *argp)
899 {
900   tts_silence();
901   if (argp && *argp)
902   {
903     ui.silent++;
904   }
905 }
906 
907 
ui_saypos(int row,int col)908 static void ui_saypos(int row, int col)
909 {
910   char buf[20];
911 
912   (void) sprintf(buf, "c%dl%d", col + 1, row + 1);
913   tts_say(buf);
914 }
915 
916 
uinit()917 void uinit()
918 {
919   (void) memset(&ui, 0, sizeof(ui));
920   ui.minrc = 4;
921   ui.curtrack = 2;
922 }
923 
924 
ui_kbwiz(int * argp)925  /*ARGSUSED*/ void ui_kbwiz(int *argp)
926 {
927   ui_funcman(&kbwiz);
928 }
929 
930 
ui_optmenu(int * argp)931  /*ARGSUSED*/ void ui_optmenu(int *argp)
932 {
933   ui_funcman(&optmenu);
934 }
935 
936 
ui_addstr(int ch)937 int ui_addstr(int ch)
938 {
939   switch (ch)
940   {
941   case 27:
942     tts_say(_("Aborting."));
943     ui_funcman(0);
944    /*FALLTHROUGH*/ case 0:
945     ui.buflen = 0;
946     break;
947 
948   case 13:
949     ui_funcman(0);
950     return (2);
951 
952   default:
953     if (ui.buflen < 99)
954     {
955       ui.buf[ui.buflen++] = ch;
956     }
957   }
958 
959   return (1);
960 }
961 
962 
ui_opt_set(int * argp)963 void ui_opt_set(int *argp)
964 {
965   int t;
966 
967   if (!argp)
968   {
969     tts_say(_("Error; no option to set.  Fix keybinding in yasr.conf."));
970     return;
971   }
972   switch (opt[*argp].type)
973   {
974   case OT_STR:
975     opt_set(*argp, argp + 1);
976     break;
977 
978   case OT_INT:
979     t = (opt_getval(*argp, 0) + 1) % (opt[*argp].v.val_int.max + 1);
980     opt_set(*argp, &t);
981     break;
982   case OT_ENUM:
983     t = (opt_getval(*argp, 0) + 1) % (opt[*argp].v.enum_max + 1);
984     opt_set(*argp, &t);
985     break;
986   default:
987     tts_say(_("error in keybinding: unsupported option type"));
988     break;
989   }
990   opt_say(*argp, 0);
991 }
992 
993 
ui_bol(int * argp)994  /*ARGSUSED*/ void ui_bol(int *argp)
995 {
996   for (rev.cc = 0; rev.cc < win->cols; rev.cc++)
997   {
998     if (!cblank(rev.cr, rev.cc))
999     {
1000       break;
1001     }
1002   }
1003   if (rev.cc == win->cols)
1004   {
1005     rev.cc = 0;
1006   }
1007   if (!cblank(rev.cr, rev.cc))
1008   {
1009     ui_sayword(rev.cr, rev.cc);
1010   }
1011 }
1012 
1013 
ui_eol(int * argp)1014  /*ARGSUSED*/ void ui_eol(int *argp)
1015 {
1016   for (rev.cc = win->cols - 1; rev.cc; rev.cc--)
1017   {
1018     if (!cblank(rev.cr, rev.cc))
1019     {
1020       break;
1021     }
1022   }
1023   if (!cblank(rev.cr, rev.cc))
1024   {
1025     ui_sayword(rev.cr, rev.cc);
1026   }
1027 }
1028 
1029 
ui_sayascii(int * argp)1030  /*ARGSUSED*/ void ui_sayascii(int *argp)
1031 {
1032   int cr;
1033   int cc;
1034 
1035   if (ui.revmode)
1036   {
1037     cr = rev.cr;
1038     cc = rev.cc;
1039   } else
1040   {
1041     cr = win->cr;
1042     cc = win->cc;
1043   }
1044   tts_say_printf("%d", win->row[cr][cc] & 0xff);
1045 }
1046