I'm writing a function/method in C that is supposed to do the following:
/** * Recieves the list of arguments and copies to conffile the name
of * the configuration file. * Returns 1 if the default file name
was used or 2 if * the parameter -f existed and therefor the
specified name was used. Returns 0 if there is a -f * parameter
but is invalid. (last argument)
*/
Basically one of these two option will be "asked" by the program:
1- controller -f FCFS|LOOK
2- controller FCFS|LOOK
If the second is asked, we enter the case of using the default name.
int get_conf_name(char* argv[], char* conffile) {
// Searches for -f
s = strstr(*argv, "-f");
if(s != NULL){
if(//its valid){
strcpy(conffile, //the name of the file which comes after -f
return 2
}
else return 0
}
else{
strcpy(confile, "config.vss")
return 1
}
}
The problem here is how do I get the word after -f and copy it to confile? And, can I access argv the same way I access conffile, since one of them is an array?
I thought of using a for loop and a strlen, but that would be a lot of unecessary work for the computer wouldn't it?
I think the assignment expects you to go through the individual arguments one by one, comparing them to "-f".
If you see no -f, you know that the default file needs to be used
If you see the flag in the final position, you know that the -f is invalid
If you see the flag in the position k, then the file name will be in argv[k+1]
The skeleton of your program should look like this:
bool foundFlag = false;
for (int i = 1 ; i < argc ; i++) {
if (strcmp(argv[i], "-f") == 0) {
if (i == argc-1) {
// Error
} else {
// argv[i+1] is the file name;
}
foundFlag = true;
break;
}
}
if (!foundFlag) {
// Default name is used
}
Related
TL;DR -- what sorts of things might cause an execvp call to not fully function/ search the path properly?
I'm on the tail end of building a rudimentary shell with some quality of life features that I've added over time e.g. history, alias's, and completions. I built those features on top of a functional shell that had a working $PATH search for execution e.g. typing in "ls -la" produced the desired behavior. As you might imagine, I accomplished this just using execvp. (This is written in C if it's not already clear)
I have not changed any of my tokenizing logic and have ensured that the file name is correct; in particular, execvp was producing the desired behavior before I had added these features to my REPL. echo "hello" still produces a tokenized char **xyz and the first token is indeed echo, null-terminated, and so on. That is, my call still looks like, with variables filled-in, ... execvp("echo", argv); after which I call perror, which should only trigger when something has gone awry. Each time I just run the above command, though, since I've added in these features, it returns a failure with the no such file or directory --- before I added these features in though, the behavior was as desired. I'll note, though, that running /bin/echo "hello" runs as expected. Examples are WLOG.
I'm not sure where I should even start looking for errors, and my Google-fu has been mostly fruitless: any suggestions?
I'm initially going to omit code because it totals to several hundred lines and a MWE would not be particularly minimal in addition to my desires to keep this general rather than very particular to my code, though I'm not sure what's causing this. My repository is public and up-to-date, and I'm happy to post any code here.
EDIT:
I knew I wasn't explicitly editing the PATH variable, etc., but this block of code was the problem:
// Grab $PATH from env
char *pathvar = getenv("PATH");
if (pathvar) {
char *path;
int i;
// tokenize on colon to get paths
// then use that immediately to
// scandir, and add everything in
// there to the completions system
path = strtok(pathvar, ":");
while (path) {
struct dirent **fListTemp;
int num_files = scandir(path, &fListTemp, NULL, alphasort);
// only adding the names that are completely composed of
// lower case letters; completions are done using a naive
// Trie Node structure that only supports lowercase letters
// for now... e.g. g++ does not work, and the '+' leads to
// a seg-fault. Same holds for . and ..
for (i = 0; i < num_files; i++) {
char *curr = fListTemp[i]->d_name;
if (strcmp(curr, ".")==0 || strcmp(curr, "..")==0){
continue;
} else if (notalpha(curr)) {
continue;
} else {
str_tolower(curr);
tn_insert(completions, curr);
}
}
for (i = 0; i < num_files; i++) {
free(fListTemp[i]);
}
free (fListTemp);
path = strtok(NULL, ":");
}
} else {
fprintf(stderr, "{wsh # init} -- $PATH variable could not be found?");
}
Note that
The getenv() function returns a pointer to the value in the
environment, or NULL if there is no match.
so my original code was indeed tampering with the PATH variable. The solution I came up with quickly was just to create a copy of that string and use that to parse through the PATH instead:
// Grab $PATH from env
char *pathvar = getenv("PATH");
char *pathvar_cpy = strcpy(pathvar_cpy, pathvar);
if (pathvar_cpy) {
char *path;
int i;
path = strtok(pathvar_cpy, ":");
while (path) {
// Scan directory
struct dirent **fListTemp;
int num_files = scandir(path, &fListTemp, NULL, alphasort);
for (i = 0; i < num_files; i++) {
char *curr = fListTemp[i]->d_name;
if (strcmp(curr, ".")==0 || strcmp(curr, "..")==0){
continue;
} else if (notalpha(curr)) {
continue;
} else {
str_tolower(curr);
tn_insert(completions, curr);
}
}
for (i = 0; i < num_files; i++) {
free(fListTemp[i]);
}
free (fListTemp);
path = strtok(NULL, ":");
}
} else {
fprintf(stderr, "{wsh # init} -- $PATH variable could not be found?");
}
I'm using fuse3 to write a file system driver that should parse all the fuse options and then take two arguments, a device path (or file containing a file system image) and a mount-point.
Is there a convenient way to use fuse's command-line parsing to extract the device from the command line? I've taken to manipulating the arguments before I hand them to fuse like this:
struct fuse_args args;
const char *device = NULL;
memset(&args, 0, sizeof(args));
for (int i = 0; i < argc; ++i)
if (i > 0 && i == argc - 2 && *argv[i] != '-')
image = argv[i];
else
fuse_opt_add_arg(&args, argv[i]);
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (!device) {
usage(argv[0]);
fuse_opt_add_arg(&args, "--help");
}
fuse_main(args.argc, args.argv, &oper, nullptr);
But this is totally gross and doesn't quite work if the user specified only one argument, unless that argument also happens to be a valid mountpoint, because fuse seems to check the mountpoint viability before printing the help.
Surely this must be a common thing to want to do, so I'm wondering what the correct idiom is for such a file system.
so for this you will have to restrict the user on the order of the cmd arguments(all fuse/mount options come before the devicepath and mountpoint). To make it simple, ensure that the path to the device and mount point are provided last:
So in your main function, this statement will check that there is a correct number of arguments and the mount point and device do not start with hyphens as with options.
if ((argc < 3) || (argv[argc-2][0] == '-') || (argv[argc-1][0] == '-'))
{
fprintf(stderr, "usage: ./myfuse [FUSE and mount options] devicepath mountPoint\n");
abort();
}
Then extract the device path and store it in a struct:
struct my_state *my_data;
my_data = malloc(sizeof(struct mi_state));
if (mi_data == NULL) {
perror("main calloc");
abort();
}
my_data->devicepath = realpath(argv[argc - 2], NULL);
argv[argc-2] = argv[argc-1];
argv[argc-1] = NULL;
argc--;
Notice that I remove the device paths from argv and decrement argc by 1 before passing hem to the fuse_main function
Then while calling the fuse_main function make sure to pass the my_data struct:
fuse_main(argc, argv, &my_fuse_operations, mi_data);
Here is the defination of the my_state struct which you can put in a header file:
struct my_state{
char *devicepath;
};
You should also add this definition in the header file below the struct definition:
#define BB_DATA ((struct mi_state *) fuse_get_context()->private_data)
And also in your init function call fuse_get_context and return BB_DATA:
void *my_init()
{
fuse_get_context();
return BB_DATA;
}
The return value will be passed in the private_data field of fuse_context to all file operations and as a parameter to the destroy() method.
The fuse_context is set up before this function is called, and fuse_get_context()->private_data returns the user_data passed to fuse_main().
I have many objects saved in a file as the following form:
{
"name":"name1",
"city":"city1",
"phone":"125698745663"
},
I have a file that content objects as these last, So I would write a function that take as parameter a file, then give us as an output a list of strings, every string is an object of the file, for example name1 city1 125698745663.
Actually I found that a little hard and I have been searching many days for that, I don't want to use libraries already exist cause I have read many documentation but they was difficult to understand for me.
I tried to write some functions.
That's my essay:
#include<stdlib.h>
/*
{
"name":"name1",
"city":"city1",
"phone":"125698745663"
}
/* Boxes of my list */
typedef_struct box
{
char* current_string;
box* next;
}box;
/* Define the structure of the list that I'll return after*/
typedef_struct list
{
box* beginning;
}list;
/*The function that return the list of the objects that existed in our files*/
**char lire (FILE* my_file) //It is possibly to put the path of the file as an argument
{
char* nameFile;
printf("enter the name of the file");
scanf("%s", &nameFile);
my_file = fopen( "nameFile","r");
if (my_file != NULL)
{
box* temp;
int i=0;
list* my_list;
int first_time = 0;
/* browse file elements */
do
{
char c = fgetc(my_file);
/* I put the following condition to get the value without its key*/
if(c == ":")
{
while(c!=",")
{
temp->current_string[i] = c;
i++;
// printf("%c",c);
c=fgetc(my_file);
}
}
/*I put this condition to pass to the following element of the list and fill it*/
if(c == "}")
{
first_time++;
/*This condition is for getting the begining of the list because it is what distinguishes the list*/
if(first_time==1)
my_list->beginning->current_string = temp->current_string;
temp = temp->next;
}
} while (c != EOF);
fclose(my_file);
}
return my_list;
}```
Well, I believe that C, especially without JSON library like cjson is not the appropriate tool to do that.
You might have a look to a list of command-line tools related to JSON:
https://ilya-sher.org/2018/04/10/list-of-json-tools-for-command-line/
I think the best is to download a command-line tool (pre-compiled if possible) and extract the information you need.
An example how to use jq:
Get outputs from jq on a single line
You can download jq from here:
https://stedolan.github.io/jq/download/
EDIT: give an example
After jq download, you can use it from the command line cmd.exe:
jq-win64.exe
jq - commandline JSON processor [version 1.6]
Usage: jq-win64.exe [options] <jq filter> [file...]
jq-win64.exe [options] --args <jq filter> [strings...]
jq-win64.exe [options] --jsonargs <jq filter> [JSON_TEXTS...]
...
test.json
[{
"name":"name1",
"city":"city1",
"phone":"125698745663"
},
{
"name":"name2",
"city":"city2",
"phone":"125698745663"
}
]
jq command line:
> jq-win64.exe ".[]|.name+\",\"+.city+\",\"+.phone" test.json
"name1,city1,125698745663"
"name2,city2,125698745663"
.[] basically means that one wants to iterate over a JSON array.
|.name basically means that one wants to extract name, add a , and .city and so on.
Hi following is the path of a file (which is stored as a string).
C:/db/OOSA/LIBRARIES/OOSA00/MS/filename.jpg
now I want only the file name from that for eg: "filename", rest should be filtered or removed.
How to do that in C?
I want to apply that file name to some other stuffs but i want to avoid .jpg extension and the path " C:/db/OOSA/LIBRARIES/OOSA00/MS/"
Below is the code:
static mgbool gbeadNApply (mgrec* db, mgrec* parent, mgrec* rec, void* gBead)
{
toolrec* toolRec = (toolrec*)gBead;
if (mgGetCode(rec) == fltXref);
{
char *xName;
parent = mgGetParent(rec);
mgGetAttList(rec,fltXrefFilename,&xName,MG_NULL);
mgSetName(parent,xName);
}
return MG_TRUE;
}
Here xName first collects the filename including path. and in mgSetName also you can see xName ( here xName assigns the collected file name along with path some thing like C:/db/OOSA/LIBRARIES/OOSA00/MS/filename.jpg. Now the thing is I want only the filename part of it to be written to mgSetName. so i want to filter xName for it.
This will be very vague, but you can probably figure out how to write a function to do this:
Write a function to find the "."
Write a function that returns, in a new string, everything before the "."
Write a function that finds the last "/" ( backslash? "\")
Write a function that removes everything before and including the "/"
you will use for loops
int find_period(const char *string)
{
if(!string) return -1;
int n;
for(n = 0; n < strlen(string); n++){
if(string[n] == '.') return n;
return -1;
}
Then you probably get the general idea.
My background is not in C (it's in Real Studio - similar to VB) and I'm really struggling to split a comma-delimited string since I'm not used to low-level string handling.
I'm sending strings to an Arduino over serial. These strings are commands in a certain format. For instance:
#20,2000,5!
#10,423,0!
'#' is the header indicating a new command and '!' is the terminating footer marking the end of a command. The first integer after '#' is the command id and the remaining integers are data (the number of integers passed as data may be anywhere from 0 - 10 integers).
I've written a sketch that gets the command (stripped of the '#' and '!') and calls a function called handleCommand() when there is a command to handle. The problem is, I really don't know how to split this command up to handle it!
Here's the sketch code:
String command; // a string to hold the incoming command
boolean commandReceived = false; // whether the command has been received in full
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// main loop
handleCommand();
}
void serialEvent(){
while (Serial.available()) {
// all we do is construct the incoming command to be handled in the main loop
// get the incoming byte from the serial stream
char incomingByte = (char)Serial.read();
if (incomingByte == '!')
{
// marks the end of a command
commandReceived = true;
return;
}
else if (incomingByte == '#')
{
// marks the start of a new command
command = "";
commandReceived = false;
return;
}
else
{
command += incomingByte;
return;
}
}
}
void handleCommand() {
if (!commandReceived) return; // no command to handle
// variables to hold the command id and the command data
int id;
int data[9];
// NOT SURE WHAT TO DO HERE!!
// flag that we've handled the command
commandReceived = false;
}
Say my PC sends the Arduino the string "#20,2000,5!". My sketch ends up with a String variable (called command) that contains "20,2000,5" and the commandRecieved boolean variable is set to True so the handleCommand() function is called.
What I would like to do in the (currently useless) handleCommand() function is assign 20 to a variable called id and 2000 and 5 to an array of integers called data, i.e: data[0] = 2000, data[1] = 5, etc.
I've read about strtok() and atoi() but frankly I just can't get my head around them and the concept of pointers. I'm sure my Arduino sketch could be optimised too.
Since you're using the Arduino core String type, strtok and other string.h functions aren't appropriate. Note that you can change your code to use standard C null-terminated strings instead, but using Arduino String will let you do this without using pointers.
The String type gives you indexOf and substring.
Assuming a String with the # and ! stripped off, finding your command and arguments would look something like this:
// given: String command
int data[MAX_ARGS];
int numArgs = 0;
int beginIdx = 0;
int idx = command.indexOf(",");
String arg;
char charBuffer[16];
while (idx != -1)
{
arg = command.substring(beginIdx, idx);
arg.toCharArray(charBuffer, 16);
// add error handling for atoi:
data[numArgs++] = atoi(charBuffer);
beginIdx = idx + 1;
idx = command.indexOf(",", beginIdx);
}
data[numArgs++] = command.substring(beginIdx);
This will give you your entire command in the data array, including the command number at data[0], while you've specified that only the args should be in data. But the necessary changes are minor.
seems to work, could be buggy:
#include<stdio.h>
#include <string.h>
int main(){
char string[]="20,2000,5";
int a,b,c;
sscanf(string,"%i,%i,%i",&a,&b,&c);
printf("%i %i %i\n",a,b,c);
a=b=c=0;
a=atoi(strtok(string,","));
b=atoi(strtok(0,","));
c=atoi(strtok(0,","));
printf("%i %i %i\n",a,b,c);
return 0;
}