Return a string on C - c

I'm getting a core dump that I have no clue how to solve. I have searched other questions and googled my problem but I just can't figure out how to solve this...
Here is the code:
const char checkExtension(const char *filename)
{
const char *point = filename;
const char *newName = malloc(sizeof(filename-5));
if((point = strrchr(filename,'.palz')) != NULL )
{
if(strstr(point,".palz") == 0)
{
strncpy(newName, filename, strlen(filename)-5);
printf("%s\n",newName ); // the name shows correctly
return newName; // Segmentation fault (core dumped)
}
}
return point;
}
The function was called char checkExtensions(const char *filename). I added the const due the solutions that I have found online but so far I haven't been able to make it work...
Thank you in advance for the help!

You have many problems with your code. Here are some of them:
Your function returns char which is a single character. You need to return a pointer to an array of characters, a C string.
You don't allocate the right amount of memory. You use sizeof() on a pointer which yields the size of a pointer.
You make it impossible for the caller to know whether or not to deallocate memory. Sometimes you heap allocate, sometimes not. Your approach will leak.
You pass '.palz', which is a character literal, to strrchr which expects a single char. What you mean to pass is '.'.
A better approach is to let the caller allocate the memory. Here is a complete program that shows how:
#include <string.h>
#include <stdio.h>
void GetNewFileName(const char *fileName, char *newFileName)
{
const char *dot = strrchr(fileName, '.');
if (dot)
{
if (strcmp(dot, ".palz") == 0)
{
size_t len = dot - fileName;
memcpy(newFileName, fileName, len);
newFileName[len] = 0;
return;
}
}
size_t len = strlen(fileName);
memcpy(newFileName, fileName, len);
newFileName[len] = 0;
return;
}
int main(void)
{
char fileName[256];
char newFileName[256];
strcpy(fileName, "foo.bar");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
strcpy(fileName, "foo.bar.palz");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
strcpy(fileName, "foo.bar.palz.txt");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
return 0;
}
Output
foo.bar foo.bar
foo.bar.palz foo.bar
foo.bar.palz.txt foo.bar.palz.txt
Note that strcmp compares sensitive to letter case. On Windows file names are insensitive to case. I will leave that issue for you to deal with.
By letting the caller allocate memory you allow them to chose where the memory is allocated. They can use a local stack allocated buffer if they like. And it's easy for the caller to allocate the memory because the new file name is never longer than the original file name.

This is most probably your problem:
const char *newName = malloc(sizeof(filename-5));
First, filename is of type const char *, which means that (filename - 5) is also of this type. Thus, sizeof(filename - 5) will always return the size of the pointer datatype of your architecture (4 for x32, 8 for x64).
So, depending on your architecture, you are calling either malloc(4) or malloc(8).
The rest of the code doesn't even compile and it has serious string manipulation issues, so it's hard to tell what you were aiming at. I suppose the strncpy() was copying too much data into newName buffer, which caused buffer overflow.
If your goal was to extract the filename from a path, then you should probably just use char *basename(char *path) for that.

Several pretty major problems with your code. Making it up as I type, so it may not fix everything first time right away. Bear with me.
You need to return a char *, not a char.
const char checkExtension(const char *filename)
{
const char *point = filename;
You malloc memory but the instruction flow does not guarantee it will be freed or returned.
sizeof(filename) should be strlen(filename), minus 5 (sans extension) but +1 (with terminating 0).
const char *newName = malloc(sizeof(filename-5));
strrchr searches for a single character. Some compilers allow "multibyte character constants", but they expect something like 2 -- not five. Since you know the length and start of the string, use strcmp. (First ensure there are at least 5 characters. If not, no use in testing anyway.)
if((point = strrchr(filename,'.palz')) != NULL ) {
Uh, strstr searches for a string inside a string and returns 0 if not found (actually NULL). This contradicts your earlier test. Remove it.
if(strstr(point,".palz") == 0)
{
strncpy copies n characters, but famously (and documented) does not add the terminating 0 if it did not get copied. You will have to this yourself.
.. This is actually where the malloc line should appear, right before using and returning it.
strncpy(newName, filename, strlen(filename)-5);
printf("%s\n",newName ); // the name shows correctly
return newName; // Segmentation fault (core dumped)
}
}
You return the original string here. How do you know you need to free it, then? If you overwrote a previous char * its memory will be lost. Better to return a duplicate of the original string (so it can always be freed), or, as I'd prefer, return NULL to indicate "no further action needed" to the calling routine.
return point;
}
Hope I did not forget anything.

There are several problems with your code:
Wrong return type:
const char checkExtension(const char *filename){
You need to return a pointer (const char *), not a single character.
Not enough memory:
const char checkExtension(const char *filename){
const char *newName = malloc(sizeof(filename-5));
You are allocating the size of a pointer (char *), which is typically 4 or 8. You need to call strlen() to find out the size of the string:
Multibyte character:
if((point = strrchr(filename,'.palz')) != NULL ) {
'.palz' is a multibyte character literal. While this is allowed in C, its value is implementation-defined and might not do what you expect. String literals use double quotes (".palz").
No terminating zero:
strncpy(newName, filename, strlen(filename)-5);
Note that strncpy() doesn't necessarily null-terminate the target string. It write at most strlen(filename)-5 characters. If the source string contains more characters (as in your case), it will not write a terminating zero.
I'm not sure what exactly you're trying to do. Perhaps something like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const char *checkExtension(const char *filename)
{
int len = strlen (filename)-5;
char *newName = NULL; /* return NULL on allocation failure. */
if (len > 0 && !strcmp (filename+len, ".palz")) {
newName = malloc (len+1);
if (newName) {
memcpy (newName, filename, len);
newName[len] = 0;
}
}
return newName;
}
int main (int ac, char **av)
{
if (ac > 1) {
const char *p = checkExtension (av[1]);
puts (p ? p : "NULL");
} else {
puts ("?");
}
return 0;
}

Multiple errors here. You have not said what you are trying to achieve, that has to be implied from the code. You have declared point and newName as const, yet reassigned with a value. You have tested strstr() == 0 when it should be strstr() == NULL. You have called strrchr(filename,'.palz') but sent a string instead of a char. Then you have returned the local variable point which goes out of scope before you get a chance to use it, because it was not declared as static. So it's irrelevant whether you returned a char or a char pointer.
char *checkExtension(const char *filename) {
// if filename has extension .palz return a pointer to
// the filename stripped of extension or return NULL
char *point;
static char newName[512];
strncpy(newName, filename, 512);
if ((point = strstr(newName, ".palz")) != NULL ) {
if (strlen (point) == 5) {
*point = 0; // string terminator
// printf("%s\n",newName ); // use only for debugging
return newName;
}
}
return NULL;
}
Alternatively provide a string the function can modify -
char *checkExtension(const char *filename, char *newName) { ... }
Alternatively provide a filename the function can modify -
char *checkExtension(char *filename) {
char *point;
if ((point = strstr(filename, ".palz")) != NULL ) {
if (strlen (point) == 5) {
*point = 0; // string terminator
return filename;
}
}
return NULL;
}

Related

How to create a copy of strcat?

I have to create a copy of some elements of the standard library in C and I have to create a copy of strcat. So I have to create a function that concatenate two strings in C. I know arrays in C can't change the allocated size. The only fonction i'm allowed to use is copies i made of strlen, strstr, and write() ... My code looks like this :
char *my_strcat(char *dest, char *src)
{
int dest_size;
int src_size;
int current_pos;
int free_space;
int pos_in_src;
src_size = my_strlen(src);
dest_size = my_strlen(dest);
while (dest[current_pos] != '\0')
current_pos = current_pos + 1;
free_space = dest_size - current_pos;
if (free_space < src_size)
return (0);
while (src[pos_in_src] != '\0')
{
dest[current_pos] = src[pos_in_src];
pos_in_src = pos_in_src + 1;
current_pos = current_pos + 1;
}
return (dest);
}
But I don't know how to declare my dest and src in the main.
I don't know how to create an array with a big size, declare it as a string like dest = "Hello\0" but this array has to still contains more than 6 characters.
Can you help me please ?
char dest[19] = "epite";
char *src = "chor42spotted";
my_strcat(dest, src);
Also, read the man for strcat(3)
the dest string must have enough space for the result.
https://linux.die.net/man/3/strcat
So your function is behaving incorrectly, you do not need to check that you have enough free space in dest
You want a function mystrcat which behaves exactly like stdlib strcat.
So the prototype is
/*
concatenate src to dest
dest [in / out] - the string to add to (buffer must be large enough)
src [in] - the string to concatenate.
Returns: dest (useless little detail for historical reasons).
*/
char *mystrcat(char *dest, const char *src);
Now we call it like this
int main(void)
{
char buff[1024]; // nice big buffer */
strcpy(buff, "Hello ");
mystrcat(buff, "world");
/* print the output to test it */
printf("%s\n", buff);
return 0;
}
But I'm not going to write mystrcat for you. That would make your homework exercise pointless.
The 1st parameter of the array simply has to be large enough to contain both strings + one null terminator. So if you for example have "hello" and "world", you need 5 + 5 +1 = 11 characters. Example:
#define LARGE_ENOUGH 11
int main (void)
{
char str[LARGE_ENOUGH] = "hello";
my_strcat(str, "world");
puts(str); // gives "helloworld"
}
In real world applications, you would typically allocate space for the array to either be same large number (couple of hundred bytes) or with a length based on strlen calls.
As for the implementation itself, your solution is needlessly complicated. Please note that the real strcat leaves all error checking to the caller. It is most likely implemented like this:
char* strcat (char* restrict s1, const char* restrict s2)
{
return strcpy(&s1[strlen(s1)], s2);
}
The most important part here is to note the const-correctness of the s2 parameter.
The restrict keywords are just micro-optimizations from the C standard, that tells the compiler that it can assume that the pointers point at different memory areas.
If you wish to roll out your own version with no library function calls just for fun, it is still rather easy, you just need two loops. Something like this perhaps:
char* lolcat (char* restrict s1, const char* restrict s2)
{
char* s1_end = s1;
while(*s1_end != '\0') // find the end of s1
{
s1_end++;
}
do // overwrite the end of s1 including null terminator
{
*s1_end = *s2;
s1_end++;
s2++;
} while(*s1_end != '\0'); // loop until the null term from s2 is copied
return s1;
}

Resolving filepath to executable in C

I am currently tasked with a homework assignment to write a primitive shell in C, and am having difficulty implementing the shell feature which constructs a path to a given requested program. e.g. Transforming user input of wc to /usr/bin/wc.
Getenv() is working fine to get the value of $PATH. Using code supplied by my instructors, I've also parsed this value into individual 'tokens,' where a token is defined: typedef char *tok_t
My question is how can I fix this implementation of the following function, which seeks to return the absolute path to a given filename if found, and NULL otherwise.
The main issue here is concatenating a tok_t and a char* to produce the full pathname.
char *resolve_path(char *filename) {
printf("trying to resolve path...\n");
char *path_var = getenv("PATH");
tok_t *path_list = get_toks(path_var);
//fprint_tok(stdout, path_list);
char *path;
for (int i = 0; path_list[i]; i++) {
path = (char*) malloc(PATH_MAX);
strcat(path, *path_list[i]);
strcat(path, filename);
printf("Trying... %s\n", path);
if (file_exists(path)) {
return path;
}
free(path);
}
return NULL;
}
Should I bother with malloc() and strcat(), or is there some better way of implementing this? Currently getting segfaults and warnings about the type compatibility in use of strcat().
You do need to use malloc() since you are returning the resulting path from the function (a pointer to an automatic array created in this function will not be valid after the function returns). You do need to use strcat() or similar in order to produce a single contiguous char * to pass to file_exists().
There are a few issues with your code, however:
Do not explicitly cast void * to other types in C - at best, it is unnecessary (I'm talking about casting the return value of your allocation, in this case).
Check to see if malloc() fails.
You don't need to call malloc() and free() inside your loop - just once (each) outside the loop is sufficient.
If tok_t is a char * then path_list is a char **, so no need to dereference path_list[i] when you pass it to strncpy()/strncat() as that would merely be a char, when they expect strings. This is the likely cause of your statement "Currently getting segfaults and warnings about the type compatibility in use of strcat()."
You need to set the first character of path to NULL before your first call to strcat(), or better, use strncpy(), in which case you will want to set the last character of path to NULL after you're done.
Use strncat() with PATH_MAX - strlen(path) because otherwise you could overflow path.
Here's an example:
char *resolve_path(const char *filename) {
printf("trying to resolve path...\n");
char *path_var = getenv("PATH");
tok_t *path_list = get_toks(path_var);
char *path = malloc(PATH_MAX+1); // See, no cast, and the +1 is for the NULL
if (!path) {
return NULL; // Check for failure
}
// strncpy/strncat won't null-terminate path if we run out of space
path[PATH_MAX] = NULL;
for (int i = 0; path_list[i]; i++) {
// this could be done more efficiently with memcpy/hand-coding/etc
strncpy(path, path_list[i], PATH_MAX); // don't dereference it
strncat(path, filename, PATH_MAX - strlen(path));
printf("Trying... %s\n", path);
if (file_exists(path)) {
return path;
}
}
free(path);
return NULL;
}
I'm expounding on #Iskar_Jarak's answer because using that code as-is still results in a segmentation fault.
Because tok_t is just a typedef for char*, it sort of muddles your code. get_toks() is (without typedef's) technically just returning a char**.
That means this line:
strncpy(path, *path_list[i], PATH_MAX);
should actually be
strncpy(path, path_list[i], PATH_MAX);
Because you shouldn't dereference path_list[i]. If you do, you pass a char tostrncpy when it takes a char*. That's why you're getting that warning and a seg fault.
So, for what it's worth, here're my corrections to your code:
char *resolve_path(const char *filename) {
printf("trying to resolve path...\n");
char *path_var = getenv("PATH");
tok_t *path_list = get_toks(path_var);
char *path = malloc(PATH_MAX+1);
if (path == NULL) {
return NULL; // malloc failed
}
path[PATH_MAX] = '\0'; // null terminate path
for (int i = 0; path_list[i]; i++) {
size_t len = strlen(path_list[i]); // get the length of this path
strncpy(path, path_list[i], PATH_MAX); // copy path_list to path
path[len] = '/'; // add seperator
strncat(path, filename, PATH_MAX - len - 1); // -1 because of separator
printf("Trying... %s\n", path);
if (file_exists(path)) {
return path;
}
}
free(path);
return NULL;
}

Splitting a string with strtok() goes wrong

I'm trying to get input from the user while allocating it dynamically and then "split" it using strtok.
Main Questions:
Im getting an infinite loop of "a{\300_\377" and ",".
Why do i get a warning of "Implicitly declaring library function "malloc"/"realoc" with type void"
Other less important questions:
3.i want to break, if the input includes "-1", how do i check it? As you can see it breaks now if its 1.
4.In the getsWordsArray() i want to return a pointer to an array of strings. Since i dont know how many strings there are do i also need to dynamically allocate it like in the getInput(). (I dont know how many chars are there in each string)
int main(int argc, const char * argv[])
{
char input = getInput();
getWordsArray(&input);
}
char getInput()
{
char *data,*temp;
data=malloc(sizeof(char));
char c; /* c is the current character */
int i; /* i is the counter */
printf ("\n Enter chars and to finish push new line:\n");
for (i=0;;i++) {
c=getchar(); /* put input character into c */
if (c== '1') // need to find a way to change it to -1
break;
data[i]=c; /* put the character into the data array */
temp=realloc(data,(i+1)*sizeof(char)); /* give the pointer some memory */
if ( temp != NULL ) {
data=temp;
} else {
free(data);
printf("Error allocating memory!\n");
return 0 ;
}
}
printf("list is: %s\n",data); // for checking
return *data;
}
void getWordsArray(char *input)
{
char *token;
char *search = " ,";
token = strtok (input,search);
while (token != NULL ) {
printf("%s\n",token);
token = strtok(NULL,search);
}
}
EDIT:
i noticed i forgot to "strtok" command so i changed it to token = strtok(NULL,search);
I still get wierd output on the printf:
\327{\300_\377
Change:
int main(int argc, const char * argv[])
{
char input = getInput();
getWordsArray(&input);
}
to:
int main(int argc, const char * argv[])
{
char *input = getInput();
getWordsArray(input);
}
with a similar to the return value of getInput():
char *getInput()
{
// ...
return data;
}
In your code, you were only saving the first character of the input string, and then passing mostly garbage to getWordsArray().
For your malloc() question, man malloc starts with:
SYNOPSIS
#include <stdlib.h>
For your getchar() question, perhaps see I'm trying to understand getchar() != EOF, etc.
Joseph answered Q1.
Q2: malloc and realoc returns type void *. You need to explicitly convert that to char *. Try this:
data = (char *) malloc(sizeof(char));
Q3: 1 can be interpreted as one character. -1, while converting to characters, is equivalent to string "-1" which has character '-' and '1'. In order to check against -1, you need to use strcmp or strncmp to compare against the string "-1".
Q4: If you are going to return a different copy, yes, dynamically allocate memory is a good idea. Alternatively, you can put all pointers to each token into a data structure like a linked list for future reference. This way, you avoid making copies and just allow access to each token in the string.
Things that are wrong:
Strings in C are null-terminated. The %s argument to printf means "just keep printing characters until you hit a '\0'". Since you don't null-terminate data before printing it, printf is running off the end of data and just printing your heap (which happens to not contain any null bytes to stop it).
What headers did you #include? Missing <stdlib.h> is the most obvious reason for an implicit declaration of malloc.
getInput returns the first char of data by value. This is not what you want. (getWordsArray will never work. Also see 1.)
Suggestions:
Here's one idea for breaking on -1: if ((c == '1') && (data[i-1] == '-'))
To get an array of the strings you would indeed need a dynamic array of char *. You could either malloc a new string to copy each token that strtok returns, or just save each token directly as a pointer into input.

C comparing pointers (with chars)

Good evening, I have 2 functions and each of them accepts as argument a pointer to char:
char pointer[255];
func1(char* pointer)
{
...
memcpy(pointer,some_char,strlen(something));
return;
}
func2(char* pointer)
{
...
if (pointer==someother_char) exit(0); //FAILs
//also I have
if(pointer==someother_pointer2char); // FAILs
}
Now I've tried strstr,strcmp etc... doesn't work. Wanted to try memcmp but I don't have static len. As I have to compare char* to char and char* to char* I would be needing two solutions right?
So, how to compare these pointers (actually pointees) in shortest possible way?
Thanks.
E D I T
Thanks to wallacer and Code Monkey now for char* to char comparison I use following:
func1(char* ptr){
char someother_char[255];
char *ptr_char = NULL; //I have to strcmp a few values so this is why I initialize it first
...
ptr_char = someother_char;
if (strcmp(ptr,ptr_char) == 0) //gtfo and it does...
...
ptr_char = some2nd;
if(strcmp...
Any suggestions maybe... (hmm external function for comparing?)
Suggestion1(by Code Monkey)
#include <stdio.h>
int main(void) {
char tempchar[255];
tempchar[0] = 'a';
tempchar[1] = 'b';
tempchar[2] = '\0';
char *ptr_char;
ptr_char = &tempchar[0];
printf("%s", ptr_char);
return 0;
}
You need to use strcmp. Not seeing how you tried to use it, this is how you should use it:
char *someother_char = "a";
char *pointer = "a";
if (strcmp(pointer, someother_char) == 0) { // match!
}
else { // not matched
}
to then do the comparison with a char, you have to promote to a char*:
char *someother_char1;
char test = 'a';
char *pointer = "a";
strncpy((char*)test,someother_char1,sizeof(test));
if (strcmp(pointer, someother_char1) == 0) { // match!
}
else { // not matched
}
if you want to use the char array then you have to de-reference:
char char_array[255];
// don't forget to fill your array
// and add a null-terminating char somewhere, such as char_array[255] = '\0';
char *ptr_somechar = &char_array[0];
char *pointer = "a";
if (strcmp(pointer, ptr_somechar) == 0) { // match!
} else { // not matched
}
Well right off the bat, if you want to compare the pointees, you need to dereference them. This means to compare the actual char value, you'll have to call
if (*pointer == someother_char)
However this will only compare the first char in the array, which is probably not what you want to do.
To compare the whole thing strcmp should work
char* someother_str = "hello strstr";
if(strcmp(pointer, someother_str) == 0) {
// do something
}
Make sure your other string is declared as a char*
More info: http://www.cplusplus.com/reference/clibrary/cstring/strcmp/
Edit: as per your comment. comparing char* and char doesn't really make sense. One is a character value, the other is an address in memory. Do do so, you can either dereference the char* or reference the value variable.
char c;
char* ptr;
// dereference ptr
if ( c == *ptr ) {
...
}
// reference the value
if ( &c == ptr ) {
}
The first method checks if the values are the same. The second checks if ptr is in fact pointing to the memory containing c ie. is ptr a pointer to c
Hope that helps
Use function srtncmp no srtcmp.
int res = strncmp(str, "¿Cuál es tu nombre? ", 100);
See the next link
compare strings
Strings are null terminated. When you use such kind of strings, it's not a good idea to mixing with other memory copy functions.
Once you do the memcpy operation, please note that your destination string will not be null terminated.
memcmp is a fast operations. Otherwise yo can simply loop through each character and quit upon finding a difference.
To use strcmp, please make sure that both the strings are null terminated. Otherwise it will lead to some crash.
I suggest you to use string functions like strcmp,strlen, strcpy to deal with strings because for that it's actually implemented.
You can't compare two pointers unless both pointers are referring to same memory location. Pointer is just a address to a memory location. What you really want to do is that, to compare the contents rather than compare the address where it's stored. So please use strcmp but again I warn you make sure that it's null terminated.

Simple C string manipulation

I trying to do some very basic string processing in C (e.g. given a filename, chop off the file extension, manipulate filename and then add back on the extension)- I'm rather rusty on C and am getting segmentation faults.
char* fname;
char* fname_base;
char* outdir;
char* new_fname;
.....
fname = argv[1];
outdir = argv[2];
fname_len = strlen(fname);
strncpy(fname_base, fname, (fname_len-4)); // weird characters at the end of the truncation?
strcpy(new_fname, outdir); // getting a segmentation on this I think
strcat(new_fname, "/");
strcat(new_fname, fname_base);
strcat(new_fname, "_test");
strcat(new_fname, ".jpg");
printf("string=%s",new_fname);
Any suggestions or pointers welcome.
Many thanks and apologies for such a basic question
You need to allocate memory for new_fname and fname_base. Here's is how you would do it for new_fname:
new_fname = (char*)malloc((strlen(outdir)+1)*sizeof(char));
In strlen(outdir)+1, the +1 part is for allocating memory for the NULL CHARACTER '\0' terminator.
In addition to what other's are indicating, I would be careful with
strncpy(fname_base, fname, (fname_len-4));
You are assuming you want to chop off the last 4 characters (.???). If there is no file extension or it is not 3 characters, this will not do what you want. The following should give you an idea of what might be needed (I assume that the last '.' indicates the file extension). Note that my 'C' is very rusty (warning!)
char *s;
s = (char *) strrchr (fname, '.');
if (s == 0)
{
strcpy (fname_base, fname);
}
else
{
strncpy (fname_base, fname, strlen(fname)-strlen(s));
fname_base[strlen(fname)-strlen(s)] = 0;
}
You have to malloc fname_base and new_fname, I believe.
ie:
fname_base = (char *)(malloc(sizeof(char)*(fname_len+1)));
fname_base[fname_len] = 0; //to stick in the null termination
and similarly for new_fname and outdir
You're using uninitialized pointers as targets for strcpy-like functions: fname_base and new_fname: you need to allocate memory areas to work on, or declare them as char array e.g.
char fname_base[FILENAME_MAX];
char new_fname[FILENAME_MAX];
you could combine the malloc that has been suggested, with the string manipulations in one statement
if ( asprintf(&new_fname,"%s/%s_text.jpg",outdir,fname_base) >= 0 )
// success, else failed
then at some point, free(new_fname) to release the memory.
(note this is a GNU extension which is also available in *BSD)
Cleaner code:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
const char *extra = "_test.jpg";
int main(int argc, char** argv)
{
char *fname = strdup(argv[1]); /* duplicate, we need to truncate the dot */
char *outdir = argv[1];
char *dotpos;
/* ... */
int new_size = strlen(fname)+strlen(extra);
char *new_fname = malloc(new_size);
dotpos = strchr(fname, '.');
if(dotpos)
*dotpos = '\0'; /* truncate at the dot */
new_fname = malloc(new_size);
snprintf(new_fname, new_size, "%s%s", fname, extra);
printf("%s\n", new_fname);
return 0;
}
In the following code I do not call malloc.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/* Change this to '\\' if you are doing this on MS-windows or something like it. */
#define DIR_SYM '/'
#define EXT_SYM '.'
#define NEW_EXT "jpg"
int main(int argc, char * argv[] ) {
char * fname;
char * outdir;
if (argc < 3) {
fprintf(stderr, "I want more command line arguments\n");
return 1;
}
fname = argv[1];
outdir = argv[2];
char * fname_base_begin = strrchr(fname, DIR_SYM); /* last occurrence of DIR_SYM */
if (!fname_base_begin) {
fname_base_begin = fname; // No directory symbol means that there's nothing
// to chop off of the front.
}
char * fname_base_end = strrchr(fname_base_begin, EXT_SYM);
/* NOTE: No need to search for EXT_SYM in part of the fname that we have cut off
* the front and then have to deal with finding the last EXT_SYM before the last
* DIR_SYM */
if (!fname_base_end) {
fprintf(stderr, "I don't know what you want to do when there is no extension\n");
return 1;
}
*fname_base_end = '\0'; /* Makes this an end of string instead of EXT_SYM */
/* NOTE: In this code I actually changed the string passed in with the previous
* line. This is often not what you want to do, but in this case it should be ok.
*/
// This line should get you the results I think you were trying for in your example
printf("string=%s%c%s_test%c%s\n", outdir, DIR_SYM, fname_base_begin, EXT_SYM, NEW_EXT);
// This line should just append _test before the extension, but leave the extension
// as it was before.
printf("string=%s%c%s_test%c%s\n", outdir, DIR_SYM, fname_base_begin, EXT_SYM, fname_base_end+1);
return 0;
}
I was able to get away with not allocating memory to build the string in because I let printf actually worry about building it, and took advantage of knowing that the original fname string would not be needed in the future.
I could have allocated the space for the string by calculating how long it would need to be based on the parts and then used sprintf to form the string for me.
Also, if you don't want to alter the contents of the fname string you could also have used:
printf("string=%s%c%*s_test%c%s\n", outdir, DIR_SYM, (unsigned)fname_base_begin -(unsigned)fname_base_end, fname_base_begin, EXT_SYM, fname_base_end+1);
To make printf only use part of the string.
The basic of any C string manipulation is that you must write into (and read from unless... ...) memory you "own". Declaring something is a pointer (type *x) reserves space for the pointer, not for the pointee that of course can't be known by magic, and so you have to malloc (or similar) or to provide a local buffer with things like char buf[size].
And you should be always aware of buffer overflow.
As suggested, the usage of sprintf (with a correctly allocated destination buffer) or alike could be a good idea. Anyway if you want to keep your current strcat approach, I remember you that to concatenate strings, strcat have always to "walk" thourgh the current string from its beginning, so that, if you don't need (ops!) buffer overflow checks of any kind, appending chars "by hand" is a bit faster: basically when you finished appending a string, you know where the new end is, and in the next strcat, you can start from there.
But strcat doesn't allow to know the address of the last char appended, and using strlen would nullify the effort. So a possible solution could be
size_t l = strlen(new_fname);
new_fname[l++] = '/';
for(i = 0; fname_base[i] != 0; i++, l++) new_fname[l] = fname_base[i];
for(i = 0; testjpgstring[i] != 0; i++, l++) new_fname[l] = testjpgstring[i];
new_fname[l] = 0; // terminate the string...
and you can continue using l... (testjpgstring = "_test.jpg")
However if your program is full of string manipulations, I suggest using a library for strings (for lazyness I often use glib)

Resources