Hello, I am having two problems with three C sursor screens I coded, using the curses library. The first problem is that a field will not highlight when I move the cursor to that field, even though I use the wattron command to highlight it. I can get the highlight command to work when I use mvwprintw, but it does not work when I use wmove. Below is a sample of code that does not highlight:

mvwprintw(win,yfmt,0,"Format: X.XXXXX");
              wattron(win,A_STANDOUT);
              if (onFloor == 1)
              {
                wmove(win,7,35);
                wattron(win,A_STANDOUT);
                onCeiling = 1;
                onFloor = 0;
                i = 1;
              }

The field at yfmt,0 in mvwprintw command is highlighted, but the field at 7,35 in the wmove command is not hightlighted. I am not sure why the wattron does not work on wmove.

The other problem I am having is that values I read in from a file are not associated with the fields that represent them is the screen. I read in values from a file and print them in the screen with the following code:

ncard=166;
      lnbeg=81*(ncard-1);
      fseek(upd,lnbeg,0);
      fgets(ppcard, 82, upd);
      sscanf(ppcard, "%lf    %lf", &tbl[0][0], &tbl[1][0]);
      mvwaddstr(win,5,3,"Solvency Tax Trigger On Value:");
      mvwaddstr(win,7,3,"Solvency Tax Trigger Off Value:");
      wrefresh(win);
      mvwprintw(win,5,35,"%5.3f",tbl[0][0]);
      mvwprintw(win,7,35,"%5.3f",tbl[1][0]);
      wrefresh(win);

When the cursor is moved to one of these two fields and the user retyoes the floating-point value in that field, the following function is called:

repl_num(); /* uses std scan to get new # */

The code in the repl_num function is:

void repl_num()
  {
  /*get user NUMERIC update*/
  int k,n,xc,xmax,nread;
  char ch;
  echo();
  /* clear current # */
  wattroff(win,A_STANDOUT);
  wmove(win,y,x);
  for(k=x;k<xt;k++)
    wprintw(win," ");
  wattron(win,A_BLINK);
  mvwprintw(win,yfmt,32,"HIT RETURN");
  wattroff(win,A_BLINK);
  wmove(win,y,x);
  wrefresh(win);
  /* get new # & check  */
  ungetch(key); /*1st digit echoed 1st when 2nd is typed*/
  nread = wscanw(win,edfmt[i],&val);
  mvwprintw(win,yfmt,32,"HIT RETURN"); /*turn off BLINK*/
  if(nread==0)
    {
    wattron(win,A_STANDOUT);
    nerrmsg("ERROR!");
    return;
    }
  wmove(win,y,x);
/* check upper, lower size limits */
  if(val>=vmax[i]) /* >= used so 1E3 out for 3 digits etc */
    {
    nerrmsg("TOO BIG!");
    return;
    }
  if(val<vmin[i])
    {
    nerrmsg("TOO LOW!");
    return;
    }
  tbl[i][j]=val;
/* clear, reprint rest of line in case user messed screen up with arrow keys
   to clear 1 col (eg side row hdgs) set nqtrs to j+1 */
   wattroff(win,A_STANDOUT);
  for(k=j,xc=x;k<nqtrs;k++,xc+=xtab)
      {
      xmax=xc+xtab;
      if(xmax>79) xmax=79;
      wmove(win,y,xc);
      for(n=xc; n<xmax; n++)
        wprintw(win," ");
      mvwprintw(win,y,xc,shofmt[i],tbl[i][k]);
      }
  wattron(win,A_STANDOUT);
  for(xt=x; xt<80 && (ch=mvwinch(win,y,xt))!=' '; xt++)
    mvwprintw(win,y,xt,"%c",ch);
  wattroff(win,A_STANDOUT);
  wmove(win,y,x-1);
  wrefresh(win);
  }

This should set the new value entered by the user to the variable that set that field, tbl[i][j]. I have verifled that the i and j values are correct for the field the user is typing in the new value. When the file is saved, I can see that the two tbl[i][j] values are still the original read-in values, not the new values typed in by the user. I do not see why the change is not associated with the variable used to set the field.

Any help with these two problems is greatly appreciated.

Recommended Answers

All 7 Replies

I am also having issues getting the header to work properly in this screen. The header prints out as this:

SOLVENCY SURCHARGE RATES

Rate # 1-10 11-20 21-30

However, if there are a few less of these taxrates, say 26 rates, the header should print out like this:

SOLVENCY SURCHARGE RATES

Rate # 1-10 11-20 21-26

I have made several attempts to get this logic to work, uisng declarations in the code:

  char *range[] = {"1-10","11-20","21-30","31-40","41-50","51-60","61-70","71-80","81"};
  char *digits[] = {"1","2","3","4","5","6","7","8","9"};
  char *digitValue;
  char tempChar;

I then try to adjust the header using the following code:

numRatesSets = (knri + 1)/10;
    lastRatesSet = 4 + (knri + 1)%10;
    shortListRatesSet = (knri + 1)%10;
    if ((knri+1)%10 == 0)
    {
      numRatesSets = numRatesSets - 1;
    }
    else
    {
      strncpy(range[numRatesSets], range[numRatesSets], 4);
      tempChar = shortListRatesSet + 48;
      *digitValue = tempChar;
      sprintf(range[numRatesSets]+4, digitValue);
      strcat(range[numRatesSets], digitValue);
    }

The knri variable is set one less than the number of rates, i.e., knri is 25 when there are 26 rates. Note that the pointers to arrays range[] and digits[] are used elsewhere in the code and work properly in that code, so I cannot change how they are declared. I get a segmentation fault when I run this code, and I have verified that this is the code causing the segmentation fault. I realize that if this worked, I would actually get 21-36 to print out, but I just want one substitution to work before I put the final code in to get the header I want. I have even tried the following:

sprintf(range[numRatesSet]+4, digitValue)

I set digitValue the same way as above when I tried the sprintf command. I apologize for the long variable names, they have meaning in the context of this function.

Any help I can get with this problem is greatly appreciated.

No comment on the curses library since I've never used it. I tried following the logic of what you are trying to do, but I can't quite get it. This line:

strncpy(range[numRatesSets], range[numRatesSets], 4);

copies a string to itself, at best. It seems to me that numRatesSets is supposed to equal 2? And you want to change it from "21-30" to "21-26"? Is that what you are going for? As far as I can tell, you want the new range to be from (2 times 10 + 1) to (25 + 1). Is that about right? How about this?

snprintf(range[numRatesSets], 6, "%d-%d", 10 * numRatesSets+1, knri+1);

The 48 in this line threw me:

tempChar = shortListRatesSet + 48;

Then I realised that '0' is 48 in ASCII. You can always just hard-code the '0' in to make it more obvious.

tempChar = shortListRatesSet + '0';

But again, I think this is all unnecessary. I think that snprintf line is what you want. On another note, I was getting segmentation faults with your original code, where range[] is defined as:

char *range[] = {"1-10","11-20","21-30","31-40","41-50","51-60","61-70","71-80","81"};

When I changed it to:

char range[9][6] = {"1-10","11-20","21-30","31-40","41-50","51-60","61-70","71-80","81"};

the segmentation faults went away. I'm not 100% sure of why. Something to consider if you are getting segmentation faults.

Not sure if that is what you were going after or not. This line in your original code really confused me:

lastRatesSet = 4 + (knri + 1)%10;

But I notice you don't seem to use it anywhere, so I didn't worry about it. I'm seeing "4" in your code a lot and I'm not sure why. But see if that snprintf line is what you want. Below is the program I made. It doesn't handle the edge cases since I got a little confused about the knri variable and the adding of 1, etc. You mentioned that range[] had to stay the same for your other code to work? Anyway, I made a temporary buffer and returned range[] to where it was originally. Not sure if that's needed.

#include <string.h>
#include <stdio.h>

int main()
{
    int knri = 25; // 26 - 1
    int i, numRatesSets;

    char range[9][6] = {"1-10","11-20","21-30","31-40","41-50","51-60","61-70","71-80","81"};
    char temp[6]; // allows temporary change of one of the range[] variables

    numRatesSets = (knri + 1)/10; // 2
    strncpy(temp, range[numRatesSets], sizeof(temp));
    snprintf(range[numRatesSets], sizeof(temp), "%d-%d", 10 * numRatesSets+1, knri+1);

    printf("Before:");
    for(i = 0; i <= numRatesSets; i++)
    {
        printf(" %s", range[i]);
    }
    // now return range[2] to what it was originally
    strncpy(range[numRatesSets], temp, sizeof(temp));
    printf("\nAfter:");
    for(i = 0; i <= numRatesSets; i++)
    {
        printf(" %s", range[i]);
    }

    return 0;
}

The knri variable is read in from a file, and is set to one less than number of tax rates for a given state. That is why you see knri + 1 in the code, that is number tax rates to be printed. The lastRatesSet = 4 + (knri + 1)%10 value is used to set the row of the screen the first rate in a column is printed, which is row 5. Code that I didn't include in my post, since it works, does that printing. Since this state has 26 tax rates, the value numRatesSets will be 2, that is correct. I tried to implement your changes, AssertNull, and I am close, but it does not quite work. I am not sure of what I am doing wrong. Here are my code changes:

The new declarations:

char *range[9][6] = {"1-10 ","11-20","21-30","31-40","41-50","51-60","61-70","71-80","81   "};
  char *digits[] = {"1","2","3","4","5","6","7","8","9"};
  char tempChar1;
  char tempChar2;
  char tempString[6];

Further down in the code:

numRatesSets = (knri + 1)/10;
    lastRatesSet = 4 + (knri + 1)%10;
    shortListRatesSet = (knri + 1)%10;
    if ((knri+1)%10 == 0)
    {
      numRatesSets = numRatesSets - 1;
    }
    else
    {
      tempChar1 = numRatesSets + '0';
      tempChar2 = shortListRatesSet + '0';
      snprintf(range[numRatesSets][3], 1, "%1c", tempChar1);
      snprintf(range[numRatesSets][4], 1, "%1c", tempChar2);
    }

The above should take the "21-30" value in the range array and turn itn to "21-26", if I am doing it right. The last step is to print the rage array across row three of the screen. I should see Rate # 1-10 11-20 21-26 on row three of the screen if I did everything right. Here is the code that does the printing:

for (iLine = 0, j = 1; iLine < 9; ++iLine, j++)
    {
      if ((iLine * 9) <= (knri + 1))
      {
        strcpy(tempString, "");
        for (mm = 0; mm < 5; mm++)
        {
          strncat(tempString, range[k][mm], 1);
        }
        mvwaddstr(win,3,xx,tempString);
        k = k + 1;
        xx = xx + 11;
      }

Unfortunately, when I ran this code, the output I ended up with is:

Rate # 11234 678

I am not sure what I did wrong, but something is not right. Note that there is other code before and after these pieces of code I have pasted in( including several lines of code after the iLine < 9 for loop), and I have verifed that code to be working. The problem lies in the code I have pasted in this reply. The last code from above is where range[k] was used before, a one-dimension array to print out the appropriate number of rate heading strings. I tried the for loop to get the the two-dimension array into one rate string heading to print out, on at a time. I also tried printing out each element of the rate heading strings one at a time, using the folowing code after initializing mm to 0:

for (iLine = 0, j = 1; iLine < 9; ++iLine, j++)
    {
      if ((iLine * 9) <= (knri + 1))
      {
        /* strcpy(tempString, "");
        for (mm = 0; mm < 5; mm++)
        {
          strncat(tempString, range[k][mm], 1);
        }
        mvwaddstr(win,3,xx,tempString); */
        mvwaddstr(win,3,xx,range[k][mm]);
        mvwaddstr(win,3,xx+1,range[k][mm+1]);
        mvwaddstr(win,3,xx+2,range[k][mm+2]);
        mvwaddstr(win,3,xx+3,range[k][mm+3]);
        mvwaddstr(win,3,xx+4,range[k][mm+4]);
        k = k + 1;
        xx = xx + 11;
      }

This gave a segmentation fault. I am not sure of what to do next.

I think I am tracking more closely now with what you are trying to do. knri is one less than the number of tax brackets. If knri is 9, 19, 29, 39, etc., no changes need to be made to the range[] string arrray. If knri's last digit is 0 through 8, add 1 and that makes it 1 through 9 and the number after the dash needs to change. Since that number is two digits, that means index 3 and 4 must change. Index 5 remains 0, denoting the terminating character of the string. Your syntax is slightly off in lines 12 and 13. It's the exact same problem in both lines. Actually, several problems.

numRatesSets = (knri + 1)/10;
    lastRatesSet = 4 + (knri + 1)%10;
    shortListRatesSet = (knri + 1)%10;
    if ((knri+1)%10 == 0)
    {
      numRatesSets = numRatesSets - 1;
    }
    else
    {
      tempChar1 = numRatesSets + '0';
      tempChar2 = shortListRatesSet + '0';
      snprintf(range[numRatesSets][3], 1, "%1c", tempChar1);
      snprintf(range[numRatesSets][4], 1, "%1c", tempChar2);
    }

Let's look at line 12. tempChar1 is '2', tempChar2 is '6'. That is calculated correctly.

snprintf(range[numRatesSets][3], 1, "%1c", tempChar1);

Let's look at the snprintf spec:

int snprintf ( char * s, size_t n, const char * format, ... );

First, it wants a char* for the first argument. You are giving it range[numRatesSets[2][3], a character. You want the ADDRESS of range[numRatesSets[2][3], so let's add an ampersand to the front.

snprintf(&range[numRatesSets][3], 1, "%1c", tempChar1);

Now for the second parameter. You are writing one character. The spec for n is as follows:

Maximum number of bytes to be used in the buffer.
The generated string has a length of at most n-1, leaving space for the additional terminating null character.

You forgot to add 1 to the number of characters to be written. The extra character is the NULL terminator. You're writing '2', then 0 (NULL terminator), for a total of TWO characters, not 1, so since you put 1 as the parameter, all it does is write the NULL terminator. Change the 1 to 2.

snprintf(&range[numRatesSets][3], 2, "%1c", tempChar1);

Finally, a character is always length 1, so the "%1c" can be changed to "%c". I'm not sure that actually matters. So lines 12 and 13 end up like so:

  snprintf(&range[numRatesSets][3], 2, "%c", tempChar1);
  snprintf(&range[numRatesSets][4], 2, "%c", tempChar2);

As for your strncat code, same problem for your second argument:

      strcpy(tempString, "");
    for (mm = 0; mm < 5; mm++)
    {
      strncat(tempString, range[k][mm], 1);
    }

It wants a string, you are giving it a character. Change it to strncat(tempString, &range[k][mm], 1);

I also don't see the point of the loop. Why not get rid of the loop and do this?

strncat(tempString, range[k], 5);

As for your segmentation fault, again, I've never used Curses, but a quick lookup of the mvwaddstr function makes me think it may be the same problem as you were having with strncat and snprintf. The function appears to want a string and you are giving it a character. Try sticking a & in front of your range[][] variable.

mvwaddstr(win,3,xx,range[k][mm]);

becomes

mvwaddstr(win,3,xx,&range[k][mm]);

I changed the following code to this:

numRatesSets = (knri + 1)/10;
    lastRatesSet = 4 + (knri + 1)%10;
    shortListRatesSet = (knri + 1)%10;
    if ((knri+1)%10 == 0)
    {
      numRatesSets = numRatesSets - 1;
    }
    else
    {
      tempChar1 = numRatesSets + '0';
      tempChar2 = shortListRatesSet + '0';
      snprintf(&range[numRatesSets][3], 2, "%c", tempChar1);
      snprintf(&range[numRatesSets][4], 2, "%c", tempChar2);
    }

and this:

if ((iLine * 9) <= (knri + 1))
      {
        strcpy(tempString, "");
        /* for (mm = 0; mm < 5; mm++)
        {
          strncat(tempString, &range[k][mm], 1);
        } */
        strncat(tempString, &range[k][mm], 5);
        mvwaddstr(win,3,xx,tempString);

This follows your suggestions, AssetNull. The output I get from these changes is:

SOLVENCY SURCHARGE RATES

Rate #

1 0.02700 0.01725 0.00175
2 0.02625 0.01625 0.00150
3 0.02525 0.01525 0.00150
4 0.02425 0.01425 0.00150
5 0.02325 0.01350 0.00150
6 0.02225 0.01100 0.00100
7 0.02125 0.00725
8 0.02025 0.00475
9 0.01925 0.00375
10 0.01825 0.00275

                          Always HIT RETURN to complete (or correct) an entry

To replace a VALUE, just type the new value
To replace single DIGIT(s), use the SPACE BAR to move cursor

You now see the entire screen I am trying to produce. As you can see, all the rate headings, which should be 1-10, 11-20, and 21-26, disappeared. I suspect it is the strncat causing this problem. I also need to mention the compilation gave me these warnings:

"solvsurchgrts.c", line 125: warning: argument #1 is incompatible with prototype:
        prototype: pointer to char : "/usr/include/iso/stdio_c99.h", line 68
        argument : pointer to pointer to char
"solvsurchgrts.c", line 126: warning: argument #1 is incompatible with prototype:
        prototype: pointer to char : "/usr/include/iso/stdio_c99.h", line 68
        argument : pointer to pointer to char
"solvsurchgrts.c", line 159: warning: argument #2 is incompatible with prototype:
        prototype: pointer to const char : "/usr/include/iso/string_iso.h", line 70
        argument : pointer to pointer to char

All three warnings are where I had the &range[ ][ ] references. Since the strncat seems to be the main problem now, I went back and changed it to this:

if ((iLine * 9) <= (knri + 1))
      {
        strcpy(tempString, "");
        for (mm = 0; mm < 5; mm++)
        {
          strncat(tempString, &range[k][mm], 1);
        }
        //strncat(tempString, &range[k][mm], 5);
        mvwaddstr(win,3,xx,tempString);

Again, I received the warnings:

"solvsurchgrts.c", line 125: warning: argument #1 is incompatible with prototype:
        prototype: pointer to char : "/usr/include/iso/stdio_c99.h", line 68
        argument : pointer to pointer to char
"solvsurchgrts.c", line 126: warning: argument #1 is incompatible with prototype:
        prototype: pointer to char : "/usr/include/iso/stdio_c99.h", line 68
        argument : pointer to pointer to char
"solvsurchgrts.c", line 159: warning: argument #2 is incompatible with prototype:
        prototype: pointer to const char : "/usr/include/iso/string_iso.h", line 70
        argument : pointer to pointer to char

The output I get from this version is:

SOLVENCY SURCHARGE RATES

Rate # 26

1 0.02700 0.01725 0.00175
2 0.02625 0.01625 0.00150
3 0.02525 0.01525 0.00150
4 0.02425 0.01425 0.00150
5 0.02325 0.01350 0.00150
6 0.02225 0.01100 0.00100
7 0.02125 0.00725
8 0.02025 0.00475
9 0.01925 0.00375
10 0.01825 0.00275

                          Always HIT RETURN to complete (or correct) an entry

To replace a VALUE, just type the new value
To replace single DIGIT(s), use the SPACE BAR to move cursor

The headings are partially there, but still not right. I do not know what I am doing wrong.

I missed this, I think, in the post before you last post. If I had seen it before my last post, I would have commented. In your code, you have a 2-D array of char*, not a 2-D array of char. You want a 2-D array of char.

char *range[9][6] = {" 1-10","11-20","21-30","31-40","41-50","51-60","61-70","71-80","81   "}; // incorrect

In mine, it's

char range[9][6] = {" 1-10","11-20","21-30","31-40","41-50","51-60","61-70","71-80","81   "}; // correct

which is what you want. Note also that I changed range[0] to " 1-10" so that the dash is always at index 3. I had not noticed that before because it hadn't come up since knri was 25. Something to consider since I believe you are assuming the dash is at index 3, correct? That solves some of your problems, not all. Look again at my replacement code when I said you could get rid of the loop with strncat.

strncat(tempString, range[k], 5); // note the lack of mm variable

I got rid of the mm variable. You left mm in there. My guess is that mm might not be 0 since you got rid of the loop, which will cause problems. Either use your loop with the mm variable or get rid of the loop AND get rid of the mm variable, but don't get rid of the loop and use the mm variable. Also, I assume that k is in the range of 0 to 8, inclusive, which would be the valid indexes?

This should all compile without any warnings.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.