Lets say I have a function:
void split_path_file(char** p, char** f, char *pf)
{
//malloc and set *p to file path, malloc and set *f to file name
//pf is the pointer to the full file and path "C:\sponge\bob\square.pants"
// edit: leave pf in its origional state
}
Whats the best way to accomplish this?
void split_path_file(char** p, char** f, char *pf) {
char *slash = pf, *next;
while ((next = strpbrk(slash + 1, "\\/"))) slash = next;
if (pf != slash) slash++;
*p = strndup(pf, slash - pf);
*f = strdup(slash);
}
(If pf == slash, then there is no directory component.)
Maybe a bit late to the party, but the best solution I have found and have been using for years is the two functions dirname and basename
path dirname basename
"/usr/lib" "/usr" "lib"
"/usr/" "/" "usr"
"usr" "." "usr"
"/" "/" "/"
They are great for separating parts of a path/filename. Together with realpath() they are IMHO unbeatable in simplicity and power.
http://linux.die.net/man/3/basename
http://man7.org/linux/man-pages/man3/realpath.3.html
Go backwards through the string until you reach the first '\\' then set *f to everything after it and *p to everything before and the '\\'.
The simplest way seems to be to start from the end and work towards the beginning, looking for the first delimiter character. You then have two cases: either you found one or you didn't. Something like this should do it for you:
#include <stdlib.h>
#include <string.h>
void split_path_file(char **p, char **f, char *pf) {
char *newcopy = malloc(strlen(pf) + 1);
strcpy(newcopy, pf);
for (z=newcopy+strlen(newcopy); z>newcopy; z--) {
if (*z == '/' || *z == '\\')
break;
}
if (z > newcopy) {
*p = newcopy;
*z = '\0';
*f = z+1;
} else {
*f = newcopy;
*p = NULL;
}
}
Update: #ephemient's comment below points out the above approach doesn't leave *p and *f suitable for calling free(). If this is important, something a little more complicated like this will be needed:
#include <stdlib.h>
#include <string.h>
void split_path_file(char **p, char **f, char *pf) {
/* Find last delimiter. */
char *z;
for (z=pf+strlen(pf); z>=pf; z--) {
if (*z == '/' || *z == '\\')
break;
}
if (z >= pf) {
/* There is a delimiter: construct separate
path and filename fragments. */
printf("--> %i\n", z-pf);
*p = malloc(z-pf+1);
strncpy(*p, pf, z-pf);
(*p)[z-pf] = '\0';
*f = malloc(strlen(z));
strcpy(*f, z+1);
} else {
/* There is no delimiter: the entire
string must be a filename. */
*p = NULL;
*f = malloc(strlen(pf)+1);
strcpy(*f, pf);
}
}
I came up with the following, of course this assumes that pf is malloced.
void split_path_file(char** p, char **f, char *pf)
{
char *posp = strrchr(pf, '\\');
*posp = '\0';
*p = strdup(pf);
*f = strdup(posp+1);
*posp = '\\';
}
Not sure if this is a better approach than above answers.
int
stripfilenameandpath (char *path, char *onlypath, char *onlyfilename)
{
/*
documentacao
path = path com path e arquivo
onlypath = somente o path
onlyfilename = somente o arquivo sem o path
*/
int ret;
int i;
int p;
char temp[255];
char *fixo;
#ifdef WIN32
const int separator = '\\';
#else
const int separator = '/';
#endif
fixo = path;
if (path == NULL)
{
if (onlypath != NULL)
{
memset (onlypath, 0, 1);
}
if (onlyfilename != NULL)
{
memset (onlyfilename, 0, 1);
}
return 1;
}
ret = strlen (path);
if (!ret)
{
if (onlypath != NULL)
{
memset (onlypath, 0, 1);
}
if (onlyfilename != NULL)
{
memset (onlyfilename, 0, 1);
}
return 0;
}
for (i = 0; i -1; i--)
{
if (temp[i] == separator)
{
temp[i + 1] = 0;
break;
}
p++;
}
p = ret - p;
fixo += p + 1;
if (onlypath != NULL)
{
strcpy (onlypath, temp);
}
if (onlyfilename != NULL)
{
strcpy (onlyfilename, fixo);
}
return 0;
}
Related
#include <stdio.h>
#include <string.h>
int main()
{
char buf[50] = "user/local/etc/bin/example.txt";
char* ptr;
ptr = strchr(buf, '/');
char path[20];
strncpy(path, buf, ptr-buf);
path[ptr-buf] =0;
printf("%s\n", path);
return 0;
}
I am able the get the substring before the first occurence of '/' i.e. I can get user but how can I get the substring after the second occurrence of '/' i.e. local and also the last occurence of '/' i.e. example without the extension .txt? How can it be done efficiently
I wouldn't use strchr for this. The problem is that strchr can only find one specific character but you care about both / and . Instead I would iterate the string using a pointer and check for both characters inside the loop..
Something like:
int main()
{
char buf[50] = "user/local/etc/bin/example.txt";
char* pStart = buf;
char* pCurrent = buf;
while(*pCurrent != '\0')
{
if (*pCurrent == '/' || *pCurrent == '.')
{
char str[20] = {0};
strncpy(str, pStart, pCurrent - pStart);
printf("%s\n", str);
pStart = pCurrent+1;
}
++pCurrent;
}
return 0;
}
Output:
user
local
etc
bin
example
If you really want to do this using strchr it could be like:
int main()
{
char buf[50] = "user/local/etc/bin/example.txt";
char* pStart = buf;
char* pCurrent = strchr(pStart, '/');
while(pCurrent != NULL)
{
char str[20] = {0};
strncpy(str, pStart, pCurrent - pStart);
printf("%s\n", str);
pStart = pCurrent+1;
pCurrent = strchr(pStart, '/');
}
pCurrent = strchr(pStart, '.');
if (pCurrent != NULL)
{
char str[20] = {0};
strncpy(str, pStart, pCurrent - pStart);
printf("%s\n", str);
}
return 0;
}
but as you can see, it requires a bit more code than the first example.
Use strtok() in this case For detailed description check this link ! Hope this helps!
Use this:
#include <stdio.h>
#include <string.h>
#define MAXLEN 100
int main(void)
{
char buf[] = "user/local/etc/bin/example.txt";
char substring[MAXLEN+1];
char *a, *b;
int len;
b = buf;
while ( (a = strchr(b, '/')) != NULL || (a = strchr(b, '.')) != NULL)
{
len = a - b;
if (len > MAXLEN)
return 0;
memcpy(substring, b, len);
substring[len] = 0;
printf("'%s'\n", substring);
b = a + 1;
}
return 0;
}
Here is the ouput:
'user'
'local'
'etc'
'bin'
'example'
So we have a path string /home/user/music/thomas.mp3.
Where is the easy way to extract file name(without extension, "thomas") and it's extension ("mp3") from this string? A function for filename, and for extension. And only GNU libc in our hands.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FILENAME_SIZE 256
char *filename(char *str) {
char *result;
char *last;
if ((last = strrchr(str, '.')) != NULL ) {
if ((*last == '.') && (last == str))
return str;
else {
result = (char*) malloc(MAX_FILENAME_SIZE);
snprintf(result, sizeof result, "%.*s", (int)(last - str), str);
return result;
}
} else {
return str;
}
}
char *extname(char *str) {
char *result;
char *last;
if ((last = strrchr(str, '.')) != NULL) {
if ((*last == '.') && (last == str))
return "";
else {
result = (char*) malloc(MAX_FILENAME_SIZE);
snprintf(result, sizeof result, "%s", last + 1);
return result;
}
} else {
return ""; // Empty/NULL string
}
}
Use basename to get the filename and then you can use something like this to get the extension.
char *get_filename_ext(const char *filename) {
const char *dot = strrchr(filename, '.');
if(!dot || dot == filename) return "";
return dot + 1;
}
Edit:
Try something like.
#include <string.h>
#include <libgen.h>
static void printFileInfo(char *path) {
char *bname;
char *path2 = strdup(path);
bname = basename(path2);
printf("%s.%s\n",bname, get_filename_ext(bname));
free(path2);
}
Regarding your actual code (all the other answers so far say to scrap that and do something else, which is good advice, however I am addressing your code as it contains blunders that it'd be good to learn about in advance of next time you try to write something).
Firstly:
strncpy(str, result, (size_t) (last-str) + 1);
is not good. You have dest and src around the wrong way; and further this function does not null-terminate the output (unless the input is short enough, which it isn't). Generally speaking strncpy is almost never a good solution to a problem; either strcpy if you know the length, or snprintf.
Simpler and less error-prone would be:
snprintf(result, sizeof result, "%.*s", (int)(last - str), str);
Similary in the other function,
snprintf(result, sizeof result, "%s", last + 1);
The snprintf function never overflows buffer and always produces a null-terminated string, so long as you get the buffer length right!
Now, even if you fixed those then you have another fundamental problem in that you are returning a pointer to a buffer that is destroyed when the function returns. You could fix ext by just returning last + 1, since that is null-terminated anyway. But for filename you have the usual set of options:
return a pointer and a length, and treat it as a length-counted string, not a null-terminated one
return pointer to mallocated memory
return pointer to static buffer
expect the caller to pass in a buffer and a buffer length, which you just write into
Finally, returning NULL on failure is probably a bad idea; if there is no . then return the whole string for filename, and an empty string for ext. Then the calling code does not have to contort itself with checks for NULL.
Here is a routine I use for that problem:
Separates original string into separate strings of path, file_name and extension.
Will work for Windows and Linux, relative or absolute style paths. Will handle directory names with embedded ".". Will handle file names without extensions.
/////////////////////////////////////////////////////////
//
// Example:
// Given path == "C:\\dir1\\dir2\\dir3\\file.exe"
// will return path_ as "C:\\dir1\\dir2\\dir3"
// Will return base_ as "file"
// Will return ext_ as "exe"
//
/////////////////////////////////////////////////////////
void GetFileParts(char *path, char *path_, char *base_, char *ext_)
{
char *base;
char *ext;
char nameKeep[MAX_PATHNAME_LEN];
char pathKeep[MAX_PATHNAME_LEN];
char pathKeep2[MAX_PATHNAME_LEN]; //preserve original input string
char File_Ext[40];
char baseK[40];
int lenFullPath, lenExt_, lenBase_;
char *sDelim={0};
int iDelim=0;
int rel=0, i;
if(path)
{ //determine type of path string (C:\\, \\, /, ./, .\\)
if( (strlen(path) > 1) &&
(
((path[1] == ':' ) &&
(path[2] == '\\'))||
(path[0] == '\\') ||
(path[0] == '/' ) ||
((path[0] == '.' ) &&
(path[1] == '/' ))||
((path[0] == '.' ) &&
(path[1] == '\\'))
)
)
{
sDelim = calloc(5, sizeof(char));
/* // */if(path[0] == '\\') iDelim = '\\', strcpy(sDelim, "\\");
/* c:\\ */if(path[1] == ':' ) iDelim = '\\', strcpy(sDelim, "\\"); // also satisfies path[2] == '\\'
/* / */if(path[0] == '/' ) iDelim = '/' , strcpy(sDelim, "/" );
/* ./ */if((path[0] == '.')&&(path[1] == '/')) iDelim = '/' , strcpy(sDelim, "/" );
/* .\\ */if((path[0] == '.')&&(path[1] == '\\')) iDelim = '\\' , strcpy(sDelim, "\\" );
/* \\\\ */if((path[0] == '\\')&&(path[1] == '\\')) iDelim = '\\', strcpy(sDelim, "\\");
if(path[0]=='.')
{
rel = 1;
path[0]='*';
}
if(!strstr(path, ".")) // if no filename, set path to have trailing delim,
{ //set others to "" and return
lenFullPath = strlen(path);
if(path[lenFullPath-1] != iDelim)
{
strcat(path, sDelim);
path_[0]=0;
base_[0]=0;
ext_[0]=0;
}
}
else
{
nameKeep[0]=0; //works with C:\\dir1\file.txt
pathKeep[0]=0;
pathKeep2[0]=0; //preserves *path
File_Ext[0]=0;
baseK[0]=0;
//Get lenth of full path
lenFullPath = strlen(path);
strcpy(nameKeep, path);
strcpy(pathKeep, path);
strcpy(pathKeep2, path);
strcpy(path_, path); //capture path
//Get length of extension:
for(i=lenFullPath-1;i>=0;i--)
{
if(pathKeep[i]=='.') break;
}
lenExt_ = (lenFullPath - i) -1;
base = strtok(path, sDelim);
while(base)
{
strcpy(File_Ext, base);
base = strtok(NULL, sDelim);
}
strcpy(baseK, File_Ext);
lenBase_ = strlen(baseK) - lenExt_;
baseK[lenBase_-1]=0;
strcpy(base_, baseK);
path_[lenFullPath -lenExt_ -lenBase_ -1] = 0;
ext = strtok(File_Ext, ".");
ext = strtok(NULL, ".");
if(ext) strcpy(ext_, ext);
else strcpy(ext_, "");
}
memset(path, 0, lenFullPath);
strcpy(path, pathKeep2);
if(rel)path_[0]='.';//replace first "." for relative path
free(sDelim);
}
}
}
Here is an old-school algorithm that will do the trick.
char path[100] = "/home/user/music/thomas.mp3";
int offset_extension, offset_name;
int len = strlen(path);
int i;
for (i = len; i >= 0; i--) {
if (path[i] == '.')
break;
if (path[i] == '/') {
i = len;
break;
}
}
if (i == -1) {
fprintf(stderr,"Invalid path");
exit(EXIT_FAILURE);
}
offset_extension = i;
for (; i >= 0; i--)
if (path[i] == '/')
break;
if (i == -1) {
fprintf(stderr,"Invalid path");
exit(EXIT_FAILURE);
}
offset_name = i;
char *extension, name[100];
extension = &path[offset_extension+1];
memcpy(name, &path[offset_name+1], offset_extension - offset_name - 1);
Then you have both information under the variables name and extension
printf("%s %s", name, extension);
This will print:
thomas mp3
I know this is old. But I tend to use strtok for things like this.
/* strtok example */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_TOKENS 20 /* Some reasonable values */
#define MAX_STRING 128 /* Easy enough to make dynamic with mallocs */
int main ()
{
char str[] ="/home/user/music/thomas.mp3";
char sep[] = "./";
char collect[MAX_TOKENS][MAX_STRING];
/* Not really necessary, since \0 is added inplace. I do this out of habit. */
memset(collect, 0, MAX_TOKENS * MAX_STRING);
char * pch = strtok (str, sep);
int ccount = 0;
if(pch != NULL) {
/* collect all seperated text */
while(pch != NULL) {
strncpy( collect[ccount++], pch, strlen(pch));
pch = strtok (NULL, sep);
}
}
/* output tokens. */
for(int i=0; i<ccount; ++i)
printf ("Token: %s\n", collect[i]);
return 0;
}
This is a rough example, and it makes it easy to deal with the tokens afterwards. Ie the last token is the extension. Second last is the basename and so on.
I also find it useful for rebuilding paths for different platforms - replace / with \.
Just a quick one: in C I have a buffer full of data like below:
char buffer[255]="CODE=12345-MODE-12453-CODE1-12355"
My question is how to search through this. For example for the CODE=12345, section bear in mind that the numbers change, so I would like to search like this CODE=***** using wildcard or preset amount of spaces after the CODE= part.
This method wont compile last one left to try
#include <stdio.h>
#include <string.h>
#include <windows.h>
int main ()
{
char buf[255]="CODE=12345-MODE-12453-CODE1-12355";
#define TRIMSPACES(p) while(*p != '\0' && isspace((unsigned char)*p) != 0) ++p
#define NSTRIP(p, n) p += n
#define STRIP(p) ++p
char* getcode(const char *input)
{
char *p = (char*) input, *buf, *pbuf;
if((buf = malloc(256)) == NULL)
return NULL;
pbuf = buf;
while(*p != '\0') {
if(strncmp(p, "CODE", 3) == 0) {
NSTRIP(p, 4); //remove 'code'
TRIMSPACES(p);//trim white-space after 'code'
if(*p != '=')
return NULL;
STRIP(p); // remove '='
TRIMSPACES(p); //trim white-spaces after '='
/* copy the value until found a '-'
note: you must be control the size of it,
for avoid overflow. we allocated size, that's 256
or do subsequent calls to realloc()
*/
while(*p != '\0' && *p != '-')
*pbuf ++ = *p++;
// break;
}
p ++;
}
//put 0-terminator.
*pbuf ++ = '\0';
return buf;
}
//
}
You could use the sscanf() function:
int number;
sscanf(buffer, "CODE = %i", &number);
for that to work well your buffer has to be null terminated.
Another way to do it instead of sscanf():
char *input, *code;
input = strstr(buf, "CODE");
if(input == NULL) {
printf("Not found CODE=\n");
return -1;
}
code = strtok(strdup(input), "=");
if(code != NULL) {
code = strtok(NULL, "-");
printf("%s\n", code); // code = atoi(code);
} else {
//not found '='
}
Or more robust way.. a bit more complex:
#define TRIMSPACES(p) while(*p != '\0' && isspace((unsigned char)*p) != 0) ++p
#define NSTRIP(p, n) p += n
#define STRIP(p) ++p
char* getcode(const char *input, size_t limit)
{
char *p = (char*) input, *buf, *pbuf;
size_t i = 0;
while(*p != '\0') {
if(strncmp(p, "CODE", 3) == 0) {
NSTRIP(p, 4); //remove 'code'
TRIMSPACES(p);//trim all white-spaces after 'code'
/* check we have a '=' after CODE (without spaces).
if there is not, returns NULL
*/
if(*p != '=')
return NULL;
/* ok. We have.. now we don't need of it
just remove it from we output string.
*/
STRIP(p);
/* remove again all white-spaces after '=' */
TRIMSPACES(p);
/* the rest of string is not valid,
because are white-spaces values.
*/
if(*p == '\0')
return NULL;
/* allocate space for store the value
between code= and -.
this limit is set into second parameter.
*/
if((buf = malloc(limit)) == NULL)
return NULL;
/* copy the value until found a '-'
note: you must be control the size of it,
for don't overflow. we allocated 256 bytes.
if the string is greater it, do implementation with
subjecents call to realloc()
*/
pbuf = buf;
while(*p != '\0' && *p != '-' && i < limit) {
*pbuf ++ = *p++;
i ++;
}
*pbuf ++ = '\0';
return buf;
}
p ++;
}
return NULL;
}
And then:
char buf[255] = "foo baa CODE = 12345-MODE-12453-CODE-12355";
char *code = getcode(buf,256);
if(code != NULL) {
printf("code = %s\n", code);
free(code);
} else {
printf("Not found code.\n");
}
output:
code = 12345
Check out this online.
if you want to don't differentiate case, you can use the strncasecmp() that's POSIX function.
Assuming the CODE= part always comes at the beginning of the string, it's pretty easy:
sscanf(buffer, "CODE = %d", &number);
...but you want buffer to be char[255], not unsigned long.
Edit: If the CODE= part isn't necessarily at the beginning of the string, you can use strstr to find CODE in the buffer, do your sscanf starting from that point, then look immediately following that:
int codes[256];
char *pos = buffer;
size_t current = 0;
while ((pos=strstr(pos, "CODE")) != NULL) {
if (sscanf(pos, "CODE = %d", codes+current))
++current;
pos += 4;
}
Edit2:
For example, you'd use this something like this:
#include <stdio.h>
#include <string.h>
#include <windows.h>
int main ()
{
// This is full of other junk as well
char buffer[255]="CODE=12345 MODE-12453 CODE=12355" ;
int i;
int codes[256];
char *pos = buffer;
size_t current = 0;
while ((pos=strstr(pos, "CODE")) != NULL) {
if (sscanf(pos, "CODE = %d", codes+current))
++current;
pos += 4;
}
for (i=0; i<current; i++)
printf("%d\n", codes[i]);
return 0;
}
For me, this produces the following output:
12345
12355
...correctly reading the two "CODE=xxx" sections, but skipings over the "MODE=yyy" section.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *getcode(const char *str, const char *pattern){
//pattern: char is match, space is skip, * is collect
static const char *p=NULL;
char *retbuf, *pat;
int i, match, skip, patlen;
if(str != NULL) p=str;
if(p==NULL || *p=='\0') return NULL;
if(NULL==(retbuf=(char*)malloc((strlen(p)+1)*sizeof(char))))
return NULL;
pat = (char*)pattern;
patlen = strlen(pat);
i = match = skip = 0;
while(*p){
if(isspace(*p)){
++p;
++skip;
continue;
}
if(*pat){
if(*p == *pat){
++match;
++p;
++pat;
} else if(*pat == '*'){
++match;
retbuf[i++]=*p++;
++pat;
} else {
if(match){//reset
pat=(char*)pattern;
p -= match + skip -1;
i = match = skip = 0;
} else //next
++p;
}
} else {
break;
}
}
if(i){//has match
retbuf[i++]='\0';
retbuf=realloc(retbuf, i);
return retbuf;
} else {
free(retbuf);
return NULL;
}
}
int main (){
char *code;
code=getcode("CODE=12345-MODE-12453-CODE1-12355", "CODE=*****");
printf("\"%s\"\n",code);//"12345"
free(code);
code=getcode(" CODE = 12345-MODE-12453-CODE1-12355", "CODE=*****");
printf("\"%s\"\n",code);//"12345"
free(code);
code=getcode("CODE-12345-MODE-12453-CODE1-12355", "CODE=*****");
if(code==NULL)printf("not match\n");//not match
code=getcode("CODE=12345-MODE-12453-CODE=12355", "CODE=*****");
printf("\"%s\"\n",code);//"12345"
free(code);
code=getcode(NULL, "CODE=*****");
printf("\"%s\"\n",code);//"12355"
free(code);
code=getcode("CODE=12345-MODE-12453-CODE1-12355", "CODE=*****");
printf("\"%s\"\n",code);//"12345"
free(code);
code=getcode(NULL, "CODE1-*****");
printf("\"%s\"\n",code);//"12355"
free(code);
return 0;
}
I have a structure
typedef struct store
{
char name[11];
int age;
} store;
and a main function(below is part of it):
int main()
{
int i=0;
int inputs;
char line[100];
char name[11];
char command[11];
store read[3000];
while(i < 3000 && gets(line) != NULL)
{
int tempage;
inputs = sscanf(line, "%10s %10s %d", command, name, &tempage);
if (inputs == 3)
{
if (strcmp(command, "register") == 0)
{
strncpy(read[i].name, name,10);
read[i].age = tempage;
i++;
....
I need to modify it so that it can read a line of arbitrary length, and store the name from the line which is also a string of arbitrary length using malloc and realloc.
How should I approach this?
What you need to do is read the line in smaller increments, and resize your buffer as you go.
As an example (not tested and not meaning to be particularly elegant, just an example):
char *readline(FILE *f)
{
char *buf = NULL;
size_t bufsz = 0, len = 0;
int keep_going = 1;
while (keep_going)
{
int c = fgetc(f);
if (c == EOF || c == '\n')
{
c = 0; // we'll add zero terminator
keep_going = 0; // and terminate the loop afterwards
}
if (bufsz == len)
{
// time to resize the buffer.
//
void *newbuf = NULL;
if (!buf)
{
bufsz = 512; // some arbitrary starting size.
newbuf = malloc(bufsz);
}
else
{
bufsz *= 2; // issue - ideally you'd check for overflow here.
newbuf = realloc(buf, bufsz);
}
if (!newbuf)
{
// Allocation failure. Free old buffer (if any) and bail.
//
free(buf);
buf = NULL;
break;
}
buf = newbuf;
}
buf[len++] = c;
}
return buf;
}
Change the name[11] to *name;
Allocate memory for that everytime using malloc.
By the way, register is a keyword in C language. You can't use it like you did !
I think what you're looking for is:
char* name;
name = (char*)malloc(sizeof(char));
This alternative approach is similar to #asveikau's, but economize on the use of malloc() by copying on the stack.
Please do not use this for homework answer.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char * slurponeline(FILE *f, int s) {
const int size = 4096;
char buffer[size];
char * r;
int c,i=0;
while( i<size && (c = fgetc(f),c>=0 && c!='\n')) buffer[i++]=c;
if (0 == s && 0 == i) return 0;
r = (size==i)? slurponeline(f,s+size):malloc(s+i);
memcpy(r+s,buffer,i);
return r;
}
int main(int argc, char ** argv) {
FILE * f = fopen(argc>1?argv[1]:"a.out","rb");
char * a,*command,*commandend,*name,*nameend;
int age;
while (a = slurponeline(f,0)) {
char * p = a;
while (*p && *p == ' ') ++p; // skip blanks.
command = p;
while (*p && *p != ' ') ++p; // skip non-blanks.
commandend = p;
while (*p && *p == ' ') ++p; // skip blanks.
name = p;
while (*p && *p != ' ') ++p; // skip non-blanks.
nameend = p;
while (*p && *p == ' ') ++p; // skip blanks.
age = atoi(p);
*commandend=0;
*nameend=0;
printf("command: %s, name: %s, age: %d\n",command,name,age);
free(a);
}
}
I am trying to get command completion working but it seems like its not working properly..
Please have a look at my code and tell me how I can fix it..
Thanks in advance...
char store_commands() {
char *newEnv;
DIR * dir;
char *new ;
struct dirent * entry;
char *env = getenv("PATH");
do {
newEnv = strsep(&env, ":");
if(newEnv != NULL)
if(strlen(newEnv) > 0) {
dir = opendir(newEnv);
if( dir == NULL ) break;
if(flag == 1) {
flag = 0;
while((entry = readdir(dir)) != NULL) {
new = malloc(strlen(entry->d_name) + 1) ;
new = strcpy(new, entry->d_name);
commands[++count] = new; // add possible commands into an array
printf("---%i %s\n", count ,commands[count]);
}
}
closedir(dir); // close directory
}
} while(newEnv);
return **commands;
}
static char** my_completion( const char * text , int start, int end){
char **matches;
store_commands();
matches = (char **)NULL;
if (start == 0)
matches = rl_completion_matches ((char*)text, &my_generator);
return matches;
}
char * dupstr (char* s) {
char *r;
r = (char*) malloc ((strlen (s) + 1));
strcpy (r, s);
return (r);
}
char* my_generator(const char* text, int state) {
int index, len;
char *comm;
if (!state) {
index = 0;
len = (int)strlen (text);
}
while ( (comm = commands[index])) {
index++;
if (strncmp (comm, text, len) == 0)
return (dupstr(comm));
}
return NULL;
}
int main (int argc, char * argv[]) {
char *command;
using_history();
rl_readline_name = basename(argv[0]);
rl_attempted_completion_function = my_completion;
while ( (command = readline(" $ "))!= NULL ) { // scan stdin
rl_bind_key('\t',rl_complete);
if(strlen(command) > 0)
add_history(command);
}
return 0;
}
Some test cases
l (tab)
Display all 1281 possibilities? (y or n) // all possibilities come up when I put one letter *** all possibilities wrong actually what I meant was all commands including the ones dont start with l
ls (tab)
ls lsbom lsdistcc lsm lso lsvfs // seems alright here
however if I press enter
comm[0]: 'ls' and comm[1]: '(null)' // execution of the command fails!!! WHY????
Execution of the command is failed
: No such file or directory
If I use a static array like this one char *test[7] = {"ls","cat","lso", "mk", "mkd", "mkdir",""}; everything seems fine including execution of the command..
Where are the definitions/declarations for commands[] and count ?
Also: your style is incoherent, the program is barely readable.
Why do you cast the return from malloc() at one place and not at another place?
If (x == 0) {} and if ( !x ) {} are equivalent. Make your choice and stick with it.
The ugly do { ... } while ( ... ); loop can be replaced by a for( ... ; ... ; ...) {} loop, saving you two levels of indentation.