NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/41257: curses: getyx + wmove violates least astonishment past end-of-line



>Number:         41257
>Category:       lib
>Synopsis:       curses: getyx + wmove violates least astonishment past 
>end-of-line
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Apr 21 06:10:00 +0000 2009
>Originator:     Jed Davis
>Release:        NetBSD 5.0_RC2
>Organization:
>Environment:
System: NetBSD planetarium.xlerb.net 5.0_RC2 NetBSD 5.0_RC2 (PLANETAR64) #3: 
Tue Mar 3 21:40:33 EST 2009 
jld%city-of-dreadful-night.xlerb.net@localhost:/usr/local/src/nb-5/sys/arch/amd64/compile/PLANETAR64
 amd64
Architecture: x86_64
Machine: amd64
>Description:

If a curses library call that advances the cursor, like waddch, advances
the cursor past the end of the line, then getyx() sets the y argument
to that line and the x argument to the column one past the right edge
of the window.  If these values are then passed back to wmove, it will
return ERR because the x coordinate is out of range.

>How-To-Repeat:

#include <curses.h>
#include <stdlib.h>
#include <string.h>

int main()
{
        WINDOW *w;
        int maxx, y, x, rc;
        char *s;

        w = initscr();
        maxx = getmaxx(w);
        s = malloc(maxx + 1);
        memset(s, 'x', maxx);
        s[maxx] = '\0';
        mvaddstr(0, 0, s);
        getyx(w, y, x);
        addch('#');
        rc = move(y, x);
        sprintf(s, "wmove(%d, %d) = %s\n", y, x, (rc == OK ? "OK" : "ERR"));
        mvaddstr(2, 0, s);
        getch();
        return OK != endwin();
}

/* Compare the behavior with that of other curses implementations. */

>Fix:

The SUSv2 says that "Some /add/ functions move the cursor just beyond
the end of the last character added.  If this position is beyond the end
of a line, it causes wrapping and scrolling ..."[*], which suggests to
me that the wrapping of the cursor position is meant to occur during the
operation that moves the cursor off the end of the line.

[*] 
http://opengroup.org/onlinepubs/007908775/xcurses/intov.html#tag_001_004_002_002

I have prepared a patch that modifies getcurx() and getcury() to report
the cursor as being at the beginning of the next line, if the past-EOL
flag is set, at http://www.NetBSD.org/~jld/nbcurses-getyx-pasteol.diff
and included below:

Index: lib/libcurses/getyx.c
===================================================================
RCS file: /bag/nb/repo/src/lib/libcurses/getyx.c,v
retrieving revision 1.5
diff -u -p -r1.5 getyx.c
--- lib/libcurses/getyx.c       28 Apr 2008 20:23:01 -0000      1.5
+++ lib/libcurses/getyx.c       21 Apr 2009 04:12:36 -0000
@@ -80,6 +80,8 @@ getparx(WINDOW *win)
 int
 getcury(WINDOW *win)
 {
+       if (win->lines[win->cury]->flags & __ISPASTEOL)
+               return win->cury + 1;
        return(win->cury);
 }
 
@@ -90,6 +92,8 @@ getcury(WINDOW *win)
 int
 getcurx(WINDOW *win)
 {
+       if (win->lines[win->cury]->flags & __ISPASTEOL)
+               return 0;
        return(win->curx);
 }

This may not be the Right Thing, but in simple testing it does appear
to work, and fix the original application that was having trouble.
Alternately, wmove() could be modified to accept one-past-the-end
coordinates as input and set up the state appropriately, but I'm not
familiar enough with the curses internals to know if that would have
unintended consequences.



Home | Main Index | Thread Index | Old Index