I'm having some problems with field validation in a ncurses form, that hopefully someone have an explanation for.
The issue is with the below attached code. Specifically in the field called 'AuthorityCode' in the function validateField().
The ENUM Auth_codes is set to it's three values and when entering correct values (one of the enum values) it works fine. It also validates most of the incorrect values (values not in the ENUM). However, for some reason it accepts a single number '1', '2', '3', '4', '5', '6' or '8' in the field, but not a '7' or '9'. It doesn't seem like any single letter gets accepted and also not any combination of more than one number, only the above numbers.
All the other validations (including other enums), seems to be working as they should.
Does anyone have an explanation for this behaviour or can find a problem in my code?
const char* Auth_codes[]
{ "Add", "Update", "Delete" };
const char* Return_codes[]
{ "Accepted", "Denied" };
const char* Priorities[]
{ "3", "5", "6", "8" };
void createForm(WINDOW *win_body, vector<string> &input, int selection)
{
int rows;
int cols;
int i;
int cury = 0;
int curx = 1;
int nb_fields;
WINDOW *inner;
formClose = false;
getmaxyx(win_body, rows, cols);
nb_fields = input.size()
fields = (FIELD**) malloc(sizeof(FIELD *) * (nb_fields + 1));
assert(fields);
for (i = 0; i < nb_fields; i++)
{
fields[i] = new_field(1, 10, cury, curx, 0, 0);
assert(fields[i] != NULL);
set_field_buffer(fields[i], 0, "");
field_opts_on(fields[i], O_ACTIVE);
field_opts_on(fields[i], O_EDIT);
field_opts_off(fields[i], O_AUTOSKIP);
set_field_back(fields[i], A_UNDERLINE);
validateField(fields[i], input[i].c_str()); // Set correct field validation based on label value
curx = 1; // Set position for next label field
cury = cury + 1; // Next line
}
fields[i] = NULL;
form = new_form(fields);
assert(form != NULL);
win_form = derwin(win_body, rows - 10, cols - 4, 3, 2);
box(win_form, 0, 0);
assert(form != NULL && win_form != NULL);
set_form_win(form, win_form);
inner = derwin(win_form, form->rows + 1, form->cols + 1, 1, 1);
assert(inner != NULL);
set_form_sub(form, inner);
assert(post_form(form) == E_OK);
assert(post_menu(menu) == E_OK);
refresh();
wrefresh(win_body);
wrefresh(win_form);
while (formClose == false)
process_input(getch());
delete_form();
}
void validateField(FIELD *field, const char *name)
{
if (strcmp(name, "AdviceNoteNumber") == 0)
{
set_field_type(field, // Field to alter
TYPE_ALNUM, // Type to associate
0); // Minimum field width
}
else if (strcmp(name, "AssignmentId") == 0 || strcmp(name, "AssignmentReferenceId") == 0
|| strcmp(name, "TransportAssignmentId") == 0)
{
set_field_type(field, // Field to alter
TYPE_INTEGER, // Type to associate
0, // Number of padding zeroes
10000, // Min value
99999); // Max value
}
else if (strcmp(name, "AuthorityCode") == 0)
{
set_field_type(field, // Field to alter
TYPE_ENUM, // Type to associate
Auth_codes, // List of values
0, // Not case sensitive
1); // Unique prefix
}
else if (strcmp(name, "ReturnCode") == 0)
{
set_field_type(field, // Field to alter
TYPE_ENUM, // Type to associate
Return_codes, // List of values
0, // Not case sensitive
1); // Unique prefix
}
else if (strcmp(name, "Priority") == 0)
{
set_field_type(field, // Field to alter
TYPE_ENUM, // Type to associate
Priorities, // List of values
0, // Not case sensitive
1); // Unique prefix
}
set_field_type(field, // Field to alter
TYPE_ENUM, // Type to associate
Priorities, // List of values
0,
1); // Unique prefix
The third parameter to set_field_type is a list of acceptable enum values, specified as a char **.
That can't be a list of pointers of some unspecified size. Clearly, the number of acceptable enum values, the number of pointers here, has to be defined in some form or fashion. It is not explicitly called out in set_field_types documentation how the number of acceptable enum values is defined, this is just a char ** parameter. But it is an accepted practice, in C and C++, to assume that a list of pointers is terminated by a NULL pointer; part of a textbook's typical definition of what NULL pointer means falls, somewhat, within those boundaries. set_field_type's documentation implies this.
const char* Priorities[]=
{ "3", "5", "6", "8" };
The parameter is a list of four char *s, that gets passed as is. By the time it makes it into set_field_type it's just some char ** value. There's nothing here that explicitly defines it as four pointers, so set_field_type tries to figure out how many enum values there is, starts looking here, counts four pointers, then keeps counting, and counting, forging ahead into random memory, attempting to interpret each value as one char * after another, until it hits something that it thinks is a NULL value, somewhere, and in the meantime attempts to interpret each intermediate value as a char *, pointing to somewhere.
If it doesn't crash by now, it'll start seeing phantom valid enum values, leading to demons flying out of your nose.
I understand how to find the size using a string type array:
char * shuffleStrings(string theStrings[])
{
int sz = 0;
while(!theStrings[sz].empty())
{
sz++;
}
sz--;
printf("sz is %d\n", sz);
char * shuffled = new char[sz];
return shuffled;
}
One of my questions in the above example also is, why do I have to decrement the size by 1 to find the true number of elements in the array?
So if the code looked like this:
char * shuffleStrings(char * theStrings[])
{
//how can I find the size??
//I tried this and got a weird continuous block of printing
int i = 0;
while(!theStrings)
{
theStrings++;
i++;
}
printf("sz is %d\n", i);
char * shuffled = new char[i];
return shuffled;
}
You should not decrement the counter to get the real size, in the fist snippet. if you have two element and one empty element, the loop will end with value , which is correct.
In the second snippet, you work on a pointer to a pointr. So the while-condition should be *theStrings (supposing that a NULL pointer ist the marker for the end of your table.
Note that in both cases, if the table would not hold the marker for the end of table, you'd risk to go out of bounds. Why not work with vector<string> ? Then you could get the size without any loop, and would not risk to go out of bounds
What you are seeing here is the "termination" character in the string or '\0'
You can see this better when you use a char* array instead of a string.
Here is an example of a size calculator that I have made.
int getSize(const char* s)
{
unsigned int i = 0;
char x = ' ';
while ((x = s[i++]) != '\0');
return i - 1;
}
As you can see, the char* is terminated with a '\0' character to indicate the end of the string. That is the character that you are counting in your algorithm and that is why you are getting the extra character.
As to your second question, seem to want to create a new array with size of all of the strings.
To do this, you could calculate the length of each string and then add them together to create a new array.
I'm having trouble with menus in ncurses. I'm trying to set up a menu, have the user select an option, and set an int called num_players depending upon their selection.
I do this with boost::lexical_cast and item_name(current_item(my_menu)) but every time I call current_item(my_menu) I'm just getting NULL.
Here's a sample of the code in question:
char *choices[] = {"1", "2", "3", "4", "5", "6"};
//create the dynamic array for the items and their description
ITEM** my_items;
MENU *my_menu;
int num_choices = 6;
my_items = new ITEM*;
for (int x = 0; x < num_choices; x++)
{
my_items[x] = new_item(choices[x], choices[x]);
}
my_items[6] = (ITEM*)NULL;
my_menu = new_menu((ITEM**)my_items);
set_menu_mark(my_menu, " * ");
set_current_item(my_menu, my_items[0]);
post_menu(my_menu);
wrefresh(scr);
int c;
while((c = wgetch(scr)) != '\n')
{ switch(c)
{ case KEY_DOWN:
menu_driver(my_menu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(my_menu, REQ_UP_ITEM);
break;
}
}
//right here, calling current_item just gives me null
//am I supposed to unpost the menu first?
//what am I doing wrong? this is frustrating
ITEM* cur = current_item(my_menu);
setNumPlayers((char*) item_name(cur));
unpost_menu(my_menu);
free_item(my_items[0]);
free_item(my_items[1]);
free_item(my_items[2]);
//etc etc
This statement:
my_items = new ITEM*;
allocates enough space for a single ITEM* and assigns the pointer to that space to my_items. Subsequently attempting to write to my_items[i] for any value of i other than 0 will overwrite random memory, which is -- to say the least -- undefined behaviour. Whatever other problems your code might have, you need to fix that before proceeding.
It's clear from the code that you expect to be able to store num_choices + 1 ITEM*s in the array, so you need to allocate an array of at least that size.
my_items = new ITEM*[num_choices + 1];
(Really, you should replace the 6 in my_items[6] = NULL; with num_choices; otherwise, you have a bug waiting to bite you.)
Don't forget to use delete[] instead of delete, when you are done.
But since you are using C++, you might as well make use of it:
std::vector<ITEM*> my_items;
for (int x = 0; x < num_choices; x++) {
my_items.emplace_back(new_item(choices[x], choices[x]));
}
my_items.emplace_back(nullptr);
/* Unfortunately, new_menu requires a non-const ITEM**,
* even though it should not modify the array. */
my_menu = new_menu(const_cast<ITEM**>(my_items.data()));
At the end, you still have to call free_item on each ITEM* before letting the std::vector destruct:
for (auto& item : my_items) free_item(item);
I'm having a weird behaviour with my C++ code. Here it is.
OI_Id * Reqlist = 0;
int * Idlist = 0;
int Reqsize = listcount; // we calculate listcount somehow earlier.
Idlist = new int [Reqsize];
if (Idlist == 0)
{
return;
}
printf ("Idlist = %0x",Idlist);
Reqlist = new OI_Id [Reqsize]; // OI_Id is a 3rd party lib simple struct.
if (Reqlist == 0)
{
return;
}
printf ("Reqlist = %0x",Reqlist);
So the problem is that in both cases it prints the same value - the same pointer is returned by the new operator. BUT! If we change the length of second allocated array to another value (Reqsize+ 1, for example), everything is OK.
Did anybody meet any similar behaviour? I have no idea what's the reason of the problem.
I'm using Ocilib to perform a bulk insert on a Oracle database but I'm having some trouble while filling a string buffer.
The documentation says:
For string/RAW arrays, the input array
MUST BE a contiguous block of data and
not an array of pointers. So to bind
an array of 10 elements for a
varchar2(30) column, binded variable
must be a like array[10][31]
And a sample proceeds to fill a buffer like this:
...
char tab_str[1000][21];
...
OCI_BindArrayOfStrings(st, ":s", (char*) tab_str, 20, 0);
...
for(i=0;i<1000;i++)
{
sprintf(tab_str[i],"Name %d",i+1);
}
...
I'm trying to fill the string buffer while looping through a std::vector of MyClass. MyClass has a std::string member.
I'm trying to use the std::string::copy method to copy over the string contents to the buffer. But I don't know exactly how to index the buffer to do it.
...
OCI_BindArrayOfStrings(st, ":f2", NULL, VCHAR_SIZE, 0);
char** vc_buffer = (char**)OCI_BindGetData(OCI_GetBind(st, 2));
...
int i = 0;
for(vector<MyClass>::const_iterator it = myVec.begin(); it != myVec.end(); ++it)
{
/* 1st try */ it->m_string.copy((vc_buffer + (i * VCHAR_SIZE)), VCHAR_SIZE);
/* 2nd try */ it->m_string.copy(vc_buffer[i], VCHAR_SIZE);
++i;
...
}
...
The first way gives me buggy data in the database. The second makes me hit a null pointer.
What I'm doing wrong?
PS:
The second approach, along the approach proposed by Alessandro Vergani below, results in null strings inserted. The first approach gives this (somewhat bizarre) result:
The gvim window shows what it's supposed to look like, the apex screen shows what ends up in the database.
(Try:
std::vector<char> tab_str(myVec.size() * (VCHAR_SIZE + 1));
...
OCI_BindArrayOfStrings(st, ":s", &tab_str[0], VCHAR_SIZE, 0);
...
int offset = 0;
for(vector<MyClass>::const_iterator it = myVec.begin(); it != myVec.end(); ++it, offset += VCHAR_SIZE)
{
it->m_string.copy(&tab_str[offset], VCHAR_SIZE);
...
}
...
I'm not sure you need to add the null terminator: if not, remove the -1 from the copy and remove the second line.