Compare argv[] to list of allowable elements - c

I want myprogram to take user-entered arguments and see if each argument matches a hard-coded list. I am looking for a better alternative to a long switch statement or a series of if else.
Here is a minimal example of what I'm attempting using enum:
$ ./myprogram blue square
//myprogram.c
#include<stdio.h>
int main(int argc, char ** argv){
//many allowable colors and shapes
enum colors_t {blue, red, /*...*/ green};
enum shape_t {square, circle, /*...*/ triangle};
//how do I check if argv[1] is on the list of colors?
if(COMPARE(argv[1], colors_t)
colors_t user_color = argv[1];
else
printf("%s is not a usable color\n",argv[1]);
//same question with shapes
if(COMPARE(argv[2], shape_t)
shape_t user_shape = argv[2];
else
printf("%s is not a usable shape\n",argv[2]);
return 0;
}
I need help with the COMPARE() function to see if argv[i] matches a member of its corresponding enum list.

One way to do this would be to use qsort and bsearch (well, if you don't mind making sure the array is sorted yourself, you don't need qsort). Something like (using your enum color_t):
struct color_picker {
char const *name;
enum colors_t id;
} acolor[] = { {"blue", blue}, {"red", red}, {"green", green} };
const unsigned acolor_count = sizeof acolor / sizeof acolor[0];
int cmp_color(struct color_picker const *l, struct color_picker const *r)
{
return strcmp(l->name, r->name);
}
int main(int argc, char *argv[])
{
struct color_picker *ptr;
qsort(acolor, acolor_count, sizeof acolor[0], cmp_color);
ptr = bsearch(name, acolor, acolor_count, sizeof acolor[0], cmp_color);
if (NULL == ptr) {
printf("%s is not a usable color\n",argv[1]); }
}
/* the value for `enum color_t` is in `ptr->id` */
return 0;
}
You could also use hash tables here, but there is not support for those in the C standard library, so you'd have to code it yourself, or use some third-party library.

Related

access struct member with a variable or pointer in C

I have a struct and want to sort an array of the struct using merge sort.
I need to pass the sorting parameter to the merge function and access the struct members.
Can this be done in C?
example:
struct movie_imdb_data {
char color[15];
char director_name[100];
int num_critic_for_reviews;
int duration; /// in min not date and time
int director_facebook_likes;
int actor_3_facebook_likes;
char actor_2_name[100];
int actor_1_facebook_likes;
int gross;
};
in the main function, I have:
if (argc > 2) {
column_sort = argv[2];
}
now I want to call merge_sort(<array of struct>, <column_sort *>)
can I access the member in the array as array[0]->column_sort to compare?
now I want to call merge sort and pass the
I want to pass the array and the sorting parameter (that I need the array sorted by) can I use a variable in place of a members name ie..
arr[1].column_sort
instead of
arr[1].color
Sounds like you want to take a command line argument specifying the name of the field to sort on and then sort on that field.
For that, try code like:
#include <stdlib.h>
#include <string.h>
/* compare function for field 3: "num_critic_for_reviews" */
int compField3(const void *a, const void *b)
{
struct movie_imdb_data* aStruct = (struct movie_imdb_data*)a;
struct movie_imdb_data* bStruct = (struct movie_imdb_data*)b;
return (aStruct->num_critic_for_reviews < bStruct->num_critic_for_reviews)?
-1: (aStruct->num_critic_for_reviews > bStruct->num_critic_for_reviews)?
+1: 0;
}
/* also define other compare functions for each field */
int main()
{
const char* columnName = argv[2];
struct movie_imdb_data* parray;
parray = your-array;
int (*comp)(const void *, const void *, void *) = 0;
/* map the column name to compare function for that column */
if (strcmp(columnName, "num_critic_for_reviews") == 0)
{
comp = compField3;
}
/* map other names to compare function for column */
else if (...) {...}
else { exit(1); /* if not recognized column name */ }
qsort(parray, numElementsOfArray, sizeof(struct movie_imdb_data), comp);
...
}
Hope this helps!
There are different approaches to your problem:
you can write separate functions to compare structures on specific fields. In main, you would select the appropriate comparison function by testing the name of the field (or possibly generic names that are not field names). You would then pass this comparison function to mergesort (or qsort...).
if all members have the same type, you could determine the offset of the field from the beginning of the structure with macros offsetof(type, member). There is no generic way to compute these offsets, you need to write a series of tests or use a table. The comparison function would use casts to access the members:
size_t member_offset = offsetof(struct movie_imdb_data, duration);
int comp_int_member(const void *a1, const void *a2) {
const int *p1 = (const int *)((const unsigned char*)a1 + member_offset);
const int *p2 = (const int *)((const unsigned char*)a2 + member_offset);
return (*p1 > *p2) - (*p1 < *p2);
}
The downside of this latter approach is it can only handle fields with a given type.

Attempting to sort an array of structs in c using the return value of ctime from a call to stat

I'm attempting to sort a directory listing based on the most recent st_mtime, and having trouble figuring out how exactly to sort the struct array I'm allocating. How can I sort the struct according to the most recent date?... here's my current approach:
static int cmpstringp(const void *p1, const void *p2){
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
struct directoryStat dStat{
char name[50];
char time[50];
};
int main(){
i = 0;
while ((ep = readdir(dp))){
stat(ep->d_name, &fileStat);
strcpy(dStat[i].name, ep->d_name);
strcpy(dStat[i].time, ctime(&fileStat.st_mtime));
i++;
}
}
qsort(dStat, i, sizeof(char), cmpstringp);
Sorting time (or datetime)-values based on their string representations might not be a good idea, because this (lexicographical) order based on a probably localization-dependent string representation of a date/time-value might then not correspond to the chronological order (cf., for example, David C. Rankin's comment). I'd store and compare the raw time_t-values instead.
See the following code, which demonstrates both, sorting by a string value and sorting by a time_t-value. Hope it helps!
typedef struct directoryStat {
char name[50];
char timeStr[50];
time_t timeVal;
}dStat;
int cmpDStatTimeStr(const void *p1, const void *p2){
return strcmp( ((dStat*)p1)->timeStr, ((dStat*)p2)->timeStr);
}
int cmpDStatTimeVal(const void *p1, const void *p2){
return ((dStat*)p1)->timeVal > ((dStat*)p2)->timeVal;
}
int main(){
dStat stat[2] = { { "the former", "2017/5/2", 14500000 }, { "the latter","2017/5/12", 14500001 }};
// sort based on string value (lexigraphical comparison):
qsort(stat, 2, sizeof(dStat), cmpDStatTimeStr);
// sort based on time value directly:
qsort(stat, 2, sizeof(dStat), cmpDStatTimeVal);
return 0;
}

adding heterogeneous members to an array

I am playing with a custom threads api, i want to spawn a new thread with two pointers to message channels and an integer.
spawn_thread(Proctype Procedure, int argc, char *argv[]);
I am using 'channels' as an shared area between threads.
i have three variables:
`Chan *return, Chan *receive and an int n'
Could i simply cast all of the values i want to store in *argv[], to chars?
The reason i haven't done this is the width of the char is only 8 bits so a max value of 255, which isn't enough to hold pointer or large values.
One common approach to storing heterogeneous data in a C array is with tagged unions:
typedef enum {chDataReturn, chDataReceive, chDataInt} ChannelDataType;
struct ChannelData {
ChannelDataType type;
union {
ChanReturn ret;
ChanReceive rcv;
int num;
} data;
};
Now your spawn_thread prototype would look as follows:
spawn_thread(Proctype Procedure, int argc, struct ChannelData *argv);
Users would put the data into the array of argvs, "tagging" each item with its type, like this:
struct ChannelData *data = malloc(3*sizeof(struct ChannelData));
data[0].type = chDataReturn;
data[0].data.ret = myReturn;
data[1].type = chDataReceive;
data[1].data.ret = myReceive;
data[2].type = chDataInt;
data[3].data.num = 12345;
spawn_thread(myProc, 3, data);
// Wait for the thread to finish, and then...
...
free(data);
char * argv[] passes a char ** which you can just as easily cast to a void ** essentially allowing you to pass an array of void * to whatever you like.
char * argv[] = {&return, &receive, &n};
spawn_thread(Procedure, 3, argv);
Procedure(int argc, char *argv[]) {
char return = *argv[0];
char receive = *argv[1];
int return = *(int *)argv[2];
// ...
}

multiple input parameters from command line in C

I am trying to pass multiple parameters when running a compiled C code
code would be like this
void main(char argc,char *argv[]){
printf("%s",argv[1]) //filename
FILE *file = fopen(argv[1], "r")
printf("%s",argv[2]) //function to be called
char* func_name = argv[2];
printf("%s",argv[3]) //how many times the function is called
int repeat = argv[3];
for(int i=0;i<repeat;i++){
func_name(file) //calls some function and passes the file to it
}
}
i would compile like this
gcc cprog.c -o cprog
run like -
./cprog textfile.txt function1 4
how do i do this ? any help would be appreciated !
First off:
You are missing some semicolons, so your code won't even compile.
argv[] are strings, so you'll have to convert them to integers if you want to use them as such.
C does not store function names in the binary, so you have to create some kind of calling table.
Below find a working example. I creates a struct that maps a name to a function, implement that function and go look for it. It's quite buggy (no input validation is done), but gives you a proof of concept on how to possibly implement this.
#include <stdlib.h>
#include <stdio.h>
struct fcn_entry {
char *name;
void (*fcn)(char *);
};
void fcn1(char *fn) {
printf("fcn1: %s\n", fn);
}
void fcn2(char *fn) {
printf("fcn2: %s\n", fn);
}
void main(char argc,char *argv[]){
// name-to-function table
struct fcn_entry entries[] = {
{ "fcn1", fcn1 },
{ "fcn2", fcn2 },
{ NULL, NULL }
};
void (*fcn_to_call)(char *);
int i = 0;
printf("%s",argv[1]); //filename
printf("%s",argv[2]); //function to be called
char* func_name = argv[2];
i = 0;
while(entries[i].name != NULL) {
if (strcmp(entries[i].name, func_name) == 0) {
fcn_to_call = entries[i].fcn;
break;
} else {
fcn_to_call = NULL;
}
i++;
}
printf("%s",argv[3]); //how many times the function is called
int repeat = atoi(argv[3]);
for(i=0;i<repeat;i++){
fcn_to_call(argv[1]);
}
}
There are a lots of error here.
int repeat = argv[3]; //You must convert char* to int before assignment.
func_name(file) //func_name is a char* not a function. C does not support reflection so there is no way to call function like this.
To be able to call a function that you have as a string, you have know which name is paired to which function.
If all functions take the same arguments, you can have an array of structures with name and function pointer, and then match the name with the correct entry in the table.
Otherwise, if the arguments are different you have to have a chain of strcmp calls to call the correct function.

how to write the character value from structure into the serial interface and convert into integer value?

struct MemoryTag1;
typedef struct MemoryTag1{
char a[8]= {'+','0','2','6','.','5','EA','\r'}; // setpoint temperature value
char b[8]= {'-','0','2','4','.','5','EB','\r'};
char c[6]= {'+','0','2','0','EC','\r'};
}Memory1;
// This is a message structure which I want to transfer over the serial interface (RS232) and later convert into integer value. please guide me in this.
Your syntax is a bit off - try this:
// declare Memory1 struct type to hold data
typedef struct MemoryTag1 {
char a[9]; // setpoint temperature value
char b[9];
char c[7];
} Memory1;
// allocate and initialise a Memory1 struct
Memory1 m = { {'+','0','2','6','.','5','E','A','\r'},
{'-','0','2','4','.','5','E','B','\r'},
{'+','0','2','0','E','C','\r'} };
Really, to be honest, I'd prefer more information. But it doesn't really matter. It only affects the method of output. If you were running this on an arduino, for instance, you could output to the serial ports as easily as:
Serial.begin(9600);
Serial.write('a');
etc, etc
As others have mentioned, there are situations in which you'd be better off using null-terminated strings. If however, you had a particular reason to do so, then I suppose you could;
#include <stdio.h>
typedef struct memoryTag1_t
{
char a[9]; // setpoint temperature value
char b[9];
char c[7];
} *pMemoryTag1_t;
typedef struct memoryTag2_t
{
char a[10]; // setpoint temperature value
char b[10];
char c[8];
} *pMemoryTag2_t;
void displayField1(char *field, int len)
{
for (int i=0; i<len; i++)
{
if (i!=0) printf(",");
printf("%c", field[i]);
}printf("\n");
}
void displayField2(char *field)
{
bool firstDone = false;
while (*field)
{
if (firstDone)
printf(",");
else
firstDone = true;
printf("%c", *field++);
}
printf("\n");
}
int main()
{
memoryTag1_t myMem1 =
{
{'+','0','2','6','.','5','E','A','\r'},
{'-','0','2','4','.','5','E','B','\r'},
{'+','0','2','0','E','C','\r'}
};
memoryTag2_t myMem2 =
{
"+026.5EA\r",
"-024.5EB\r",
"+020EC\r"
};
displayField1(myMem1.a, sizeof(myMem1.a));
displayField1(myMem1.b, sizeof(myMem1.b));
displayField1(myMem1.c, sizeof(myMem1.c));
displayField2(myMem2.a);
displayField2(myMem2.b);
displayField2(myMem2.c);
}
Output:
(Don't forget there's a \r printed 'after' the last comma in each line)
+,0,2,6,.,5,E,A,
-,0,2,4,.,5,E,B,
+,0,2,0,E,C,
+,0,2,6,.,5,E,A,
-,0,2,4,.,5,E,B,
+,0,2,0,E,C,
you can not declare struct in C in this way:
it should be
typedef struct MemoryTag1{
char a[9];
char b[9];
char c[7];
}Memory1;
you can set value in the declaration of an object of this structure:
Memory1 test = {
{'+','0','2','6','.','5','E','A','\r'},
{'-','0','2','4','.','5','E','B','\r'},
{'+','0','2','0','E','C','\r'}
};
If you use this bloc in each initiation of a Memory1 object so you can use macro to make it easier:
#define INIT_MEMORYTAG1 {\
{'+','0','2','6','.','5','E','A','\r'},\
{'-','0','2','4','.','5','E','B','\r'},\
{'+','0','2','0','EC','\r'}\
}
and then in your declaration of a Memory1 object:
Memory1 test = INIT_MEMORYTAG1;
BTW: You can not put 'EA', 'EB', 'EC' like a 1 charachter you have to separate them to:
'E','A', 'E','B', 'E','C' and so you have to update your char array sizes in the struct definition
That is really not C syntax.
You can't have initializers in declarations of types, that doesn't have any meaning.
You need to do it like this, to build the message:
typedef struct {
char a[10];
char b[10];
char c[8];
} Memory1;
int main(void)
{
Memory1 m1;
strcpy(m1.a, "+026.5EA\r");
strcpy(m1.b, "-024.5EB\r");
strcpy(m1.c, "+020EC\r");
return 0;
}
Note that the above will build proper C strings in the fields of the message, i.e. there will be 0-characters acting as terminators. The sizes were too small, so I changed that.
It's trivial to ignore the terminator characters if you need to send this over some format that doesn't allow them; send each field separately.
Converting one of the fields back into integers could be done using sscanf(), for instance.

Resources