473,750 Members | 2,270 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Separating directory from file name: Code Review!

I've not used C much before, so I don't know how robust or good this code
is. I'd appreciate any feedback or criticisms anyone has!

Thanks,
Joe

#include <stdio.h>
#include <string.h>

#define BUF_LENGTH 1000

int main()
{
char *string = "/some/file/here/file_name";

/* File should be "/file_name", and "file_name" after incrementing */
char* file_name = strrchr( string, '/');
file_name++;

printf ("The file name is %s\n", file_name);

/* Initialize the path */
char *path = (char*)malloc(B UF_LENGTH);
memset(path, 0, BUF_LENGTH);

/* The length of the file path should be the difference between the
* location of file pointer and the string pointer. I can't always
assume
* this, right? */
int length_of_file_ path = file_name - string;

/* Put the first length_of_file_ path characters into path */
strncat(path, string, length_of_file_ path);

printf("The file path is %s\n", path);

}
Nov 14 '05 #1
17 2332
On Wed, 2 Jun 2004 00:48:45 GMT, "Joe Laughlin"
<Jo************ ***@boeing.com> wrote in comp.lang.c:
I've not used C much before, so I don't know how robust or good this code
is. I'd appreciate any feedback or criticisms anyone has!

Thanks,
Joe
Oops, I just answered your original question suggesting the use of
strrchr().
#include <stdio.h>
#include <string.h>
Need to add:

#include <stdlib.h>

....here to have a proper prototype for malloc() in scope.
#define BUF_LENGTH 1000
Did you know that <stdio.h> defines a macro FILENAME_MAX? While I
don't know of any system in common use today that allows a path name
to exceed 1000 characters, if you use FILENAME_MAX the buffer will
always be large enough for any legal file name on any system where it
is compiled.

int main()
{
char *string = "/some/file/here/file_name";

/* File should be "/file_name", and "file_name" after incrementing */
char* file_name = strrchr( string, '/');
You need to check for NULL here. While strrchr() can't return a null
pointer in this example, I assume in the real world you will want to
work with arbitrary strings entered interactively by a user or read
from files or the command line. Sooner or later your program will be
handed a string that does not contain a '/' character. Then when you
increment the null pointer or pass it to printf() or strncat() you
generate undefined behavior, most likely causing your operating system
to terminate your program.
file_name++;

printf ("The file name is %s\n", file_name);

/* Initialize the path */
char *path = (char*)malloc(B UF_LENGTH);
Never cast the return value of malloc() in C. If you did this to shut
up compiler warnings, we fixed that properly by including <stdlib.h>
for malloc's prototype. Without that prototype, the cast eliminates
the warning but specifically does the wrong thing on some platforms.
memset(path, 0, BUF_LENGTH);
First, you don't need to set the entire buffer to 0 for what you are
doing. Second, if you do need to allocate memory for an array of
characters and have it all zeroed, calloc() will do both operations in
one call. Note that calloc() is guaranteed to initialize an array of
characters to all valid '\0' values, it is not so guaranteed to do so
with any other type.

But all you really need is one '\0' at the beginning, so you could
just code:

*path = '\0';
/* The length of the file path should be the difference between the
* location of file pointer and the string pointer. I can't always
assume
* this, right? */
int length_of_file_ path = file_name - string;
The length of the string prior to the final '/', excluding the final
'/' but allowing for a '\0' in its place to terminate it, is exactly
file_name - string. It can't ever be anything else (if strrchr() does
not return NULL). You can always assume this.
/* Put the first length_of_file_ path characters into path */
strncat(path, string, length_of_file_ path);
The problem with this is length_of_file_ path includes the final '/',
which you do not want to appear in the path string (or do you?). If
you don't, use length_of_file_ path - 1 as the length argument.
printf("The file path is %s\n", path);

}


--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.l earn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #2
In article <Hy********@new s.boeing.com>,
"Joe Laughlin" <Jo************ ***@boeing.com> wrote:
I've not used C much before, so I don't know how robust or good this code
is. I'd appreciate any feedback or criticisms anyone has!

Thanks,
Joe

#include <stdio.h>
#include <string.h>

#define BUF_LENGTH 1000

int main()
{
char *string = "/some/file/here/file_name";

/* File should be "/file_name", and "file_name" after incrementing */
char* file_name = strrchr( string, '/');
file_name++;
There is no guarantee that there is a '/' in file_name. What will happen
then?
printf ("The file name is %s\n", file_name);

/* Initialize the path */
char *path = (char*)malloc(B UF_LENGTH);
memset(path, 0, BUF_LENGTH);
There is no guarantee that the filename is limited to 1000 characters.
/* The length of the file path should be the difference between the
* location of file pointer and the string pointer. I can't always
assume
* this, right? */
int length_of_file_ path = file_name - string;

/* Put the first length_of_file_ path characters into path */
strncat(path, string, length_of_file_ path);

printf("The file path is %s\n", path);

}


Goes horribly wrong on my Macintosh with filenames like
"CB:Documents:L etters:Letter of 15/May/2004".
Nov 14 '05 #3
Jack Klein <ja*******@spam cop.net> writes:
Did you know that <stdio.h> defines a macro FILENAME_MAX? While I
don't know of any system in common use today that allows a path name
to exceed 1000 characters,
On Linux, FILENAME_MAX is 4096. In fact, I don't know of any Unix
system in common use today that does /not/ allow a path name of at least
1024 characters.
if you use FILENAME_MAX the buffer will always be large enough for any
legal file name on any system where it is compiled.


What if the system poses no arbitrary limit on the maximum file name
size? If I take the standard literally (and ignore the non-normative
footnote), `FILENAME_MAX' must be defined to the size of the whole
address space on such systems.

Martin
--
,--. Martin Dickopp, Dresden, Germany ,= ,-_-. =.
/ ,- ) http://www.zero-based.org/ ((_/)o o(\_))
\ `-' `-'(. .)`-'
`-. Debian, a variant of the GNU operating system. \_/
Nov 14 '05 #4
Martin Dickopp wrote:
Jack Klein <ja*******@spam cop.net> writes:
Did you know that <stdio.h> defines a macro
FILENAME_MAX? While I don't know of any system in
common use today that allows a path name to exceed 1000
characters,


On Linux, FILENAME_MAX is 4096. In fact, I don't know of
any Unix system in common use today that does /not/ allow
a path name of at least 1024 characters.
if you use FILENAME_MAX the buffer will always be large
enough for any legal file name on any system where it is
compiled.


What if the system poses no arbitrary limit on the
maximum file name size? If I take the standard literally
(and ignore the non-normative footnote), `FILENAME_MAX'
must be defined to the size of the whole address space on
such systems.

Martin


non-normative footnote?
Nov 14 '05 #5
Jack Klein wrote:
On Wed, 2 Jun 2004 00:48:45 GMT, "Joe Laughlin"
<Jo************ ***@boeing.com> wrote in comp.lang.c:
I've not used C much before, so I don't know how robust
or good this code is. I'd appreciate any feedback or
criticisms anyone has!

Thanks,
Joe
Oops, I just answered your original question suggesting
the use of strrchr().
#include <stdio.h>
#include <string.h>


Need to add:

#include <stdlib.h>


Noted.

...here to have a proper prototype for malloc() in scope.
#define BUF_LENGTH 1000
Did you know that <stdio.h> defines a macro FILENAME_MAX?
While I don't know of any system in common use today that
allows a path name to exceed 1000 characters, if you use
FILENAME_MAX the buffer will always be large enough for
any legal file name on any system where it is compiled.


Noted again.

int main()
{
char *string = "/some/file/here/file_name";

/* File should be "/file_name", and "file_name"
after incrementing */ char* file_name = strrchr(
string, '/');


You need to check for NULL here. While strrchr() can't
return a null pointer in this example, I assume in the
real world you will want to work with arbitrary strings
entered interactively by a user or read from files or the
command line. Sooner or later your program will be
handed a string that does not contain a '/' character.
Then when you increment the null pointer or pass it to
printf() or strncat() you generate undefined behavior,
most likely causing your operating system to terminate
your program.


Good point...
file_name++;

printf ("The file name is %s\n", file_name);

/* Initialize the path */
char *path = (char*)malloc(B UF_LENGTH);
Never cast the return value of malloc() in C. If you did
this to shut up compiler warnings, we fixed that properly
by including <stdlib.h> for malloc's prototype. Without
that prototype, the cast eliminates the warning but
specifically does the wrong thing on some platforms.


Why shouldn't I cast the return value of malloc()?
memset(path, 0, BUF_LENGTH);
First, you don't need to set the entire buffer to 0 for
what you are doing. Second, if you do need to allocate
memory for an array of characters and have it all zeroed,
calloc() will do both operations in one call. Note that
calloc() is guaranteed to initialize an array of
characters to all valid '\0' values, it is not so
guaranteed to do so with any other type.


calloc()? I tried 'man calloc' on my linux system, but it didn't find the
man page.

But all you really need is one '\0' at the beginning, so
you could just code:

*path = '\0';
/* The length of the file path should be the
difference between the * location of file pointer
and the string pointer. I can't always assume
* this, right? */
int length_of_file_ path = file_name - string;
The length of the string prior to the final '/',
excluding the final '/' but allowing for a '\0' in its
place to terminate it, is exactly file_name - string. It
can't ever be anything else (if strrchr() does not return
NULL). You can always assume this.


Ok. I was wondering what would happen if the character array was not stored
in one contigous block in memory (i.e. if it were really long). The
subtracting the two addresses of the pointers would not give me the correct
length.
/* Put the first length_of_file_ path characters into
path */ strncat(path, string, length_of_file_ path);


The problem with this is length_of_file_ path includes the
final '/', which you do not want to appear in the path
string (or do you?). If you don't, use
length_of_file_ path - 1 as the length argument.


I did want the final '/' to be included.
printf("The file path is %s\n", path);

}


Thanks for your help!

Nov 14 '05 #6
"Joe Laughlin" <Jo************ ***@boeing.com> writes:
Martin Dickopp wrote:
Jack Klein <ja*******@spam cop.net> writes:
Did you know that <stdio.h> defines a macro
FILENAME_MAX? While I don't know of any system in
common use today that allows a path name to exceed 1000
characters,


On Linux, FILENAME_MAX is 4096. In fact, I don't know of
any Unix system in common use today that does /not/ allow
a path name of at least 1024 characters.
if you use FILENAME_MAX the buffer will always be large
enough for any legal file name on any system where it is
compiled.


What if the system poses no arbitrary limit on the
maximum file name size? If I take the standard literally
(and ignore the non-normative footnote), `FILENAME_MAX'
must be defined to the size of the whole address space on
such systems.


non-normative footnote?


The standard defines `FILENAME_MAX' in 7.19.1#3:

| [...]
|
| FILENAME_MAX
|
| which expands to an integer constant expression that is the size
| needed for an array of char large enough to hold the longest file
| name string that the implementation guarantees can be opened; [...]

Attached to that text is a footnote which reads

| If the implementation imposes no practical limit on the length of
| file name strings, the value of FILENAME_MAX should instead be the
| recommended size of an array intended to hold a file name string. Of
| course, file name string contents are subject to other system-specific
| constraints; therefore all possible strings of length FILENAME_MAX
| cannot be expected to be opened successfully.

However, footnotes in the standard are not normative.

Martin
--
,--. Martin Dickopp, Dresden, Germany ,= ,-_-. =.
/ ,- ) http://www.zero-based.org/ ((_/)o o(\_))
\ `-' `-'(. .)`-'
`-. Debian, a variant of the GNU operating system. \_/
Nov 14 '05 #7

"Martin Dickopp" <ex************ ****@zero-based.org> wrote
What if the system poses no arbitrary limit on the maximum file
name size? If I take the standard literally (and ignore the non-
normative footnote), `FILENAME_MAX' must be defined to the
size of the whole address space on such systems.

This of course is the problem with trying to be totally portable.

People want to do the following

void foo(char *directory)
{
char fname[FILENAME_MAX];

strcpy(fname, directory);
strcat(fname, "afile.name ");
fopen(fname);
}

If FILENAME_MAX is too large then this will overflow the stack, whilst if it
is too small you have UB. The code is perfect on a system where a legal
directory will never exceed the limit, and "good enough" for most systems,
but still leaves open potential security holes if the OS somehow allows
arbitrarily long paths to get into the "directory" pointer
Nov 14 '05 #8
Martin Dickopp <ex************ ****@zero-based.org> writes:
"Joe Laughlin" <Jo************ ***@boeing.com> writes:
Martin Dickopp wrote:
Jack Klein <ja*******@spam cop.net> writes:

Did you know that <stdio.h> defines a macro
FILENAME_MAX? While I don't know of any system in
common use today that allows a path name to exceed 1000
characters,

On Linux, FILENAME_MAX is 4096. In fact, I don't know of
any Unix system in common use today that does /not/ allow
a path name of at least 1024 characters.

if you use FILENAME_MAX the buffer will always be large
enough for any legal file name on any system where it is
compiled.

What if the system poses no arbitrary limit on the
maximum file name size? If I take the standard literally
(and ignore the non-normative footnote), `FILENAME_MAX'
must be defined to the size of the whole address space on
such systems.


non-normative footnote?


The standard defines `FILENAME_MAX' in 7.19.1#3:

| [...]
|
| FILENAME_MAX
|
| which expands to an integer constant expression that is the size
| needed for an array of char large enough to hold the longest file
| name string that the implementation guarantees can be opened; [...]

[...]

Suppose I write a conforming C implementation (compiler and library)
for some operating system. Suppose the OS itself supports file names
up to, say, 8192 characters (passing a longer string to the OS's "open
file" routine causes an error), but I'm only willing to guarantee up
to 512 characters. Then I can set FILENAME_MAX to 512. Even though
fopen() will happy handle file names up to 8192 characters the
implementation only guarantees 512.

I'm not convinced that FILENAME_MAX is particularly useful.

--
Keith Thompson (The_Other_Keit h) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #9
On Wed, 2 Jun 2004 17:20:49 GMT, "Joe Laughlin"
<Jo************ ***@boeing.com> wrote in comp.lang.c:
Jack Klein wrote:
On Wed, 2 Jun 2004 00:48:45 GMT, "Joe Laughlin"
<Jo************ ***@boeing.com> wrote in comp.lang.c:
#include <stdio.h>
#include <string.h>


Need to add:

#include <stdlib.h>


Noted.

...here to have a proper prototype for malloc() in scope.
[snip]
char *path = (char*)malloc(B UF_LENGTH);


Never cast the return value of malloc() in C. If you did
this to shut up compiler warnings, we fixed that properly
by including <stdlib.h> for malloc's prototype. Without
that prototype, the cast eliminates the warning but
specifically does the wrong thing on some platforms.


Why shouldn't I cast the return value of malloc()?


Casting the return value of malloc() is:

1. Not necessary in C as the memory allocation functions (malloc,
calloc, and realloc) all return a pointer to void. In C, a pointer to
void may be assigned to a pointer to any object type directly, with no
cast.

2. Prevents a required diagnostic if a proper prototype for malloc is
not in scope, as in this case where <stdlib.h> was not included.
Prototypes, or at least declarations, for all functions are required
in the latest (1999) version of the C standard, but most compilers
today still operate according to an earlier standard version. Without
a declaration, the compiler must assume that the function returns an
int and accepts whatever arguments (after default promotions) that you
pass to it. Without the cast the compiler is required to issue a
diagnostic because assigning the assumed int returned by malloc to a
pointer without a cast is a constraint violation, thus warning you
that you have missed something.

3. By preventing the diagnostic that would cause you to fix the
problem properly (by including <stdlib.h>), this can cause some very
nasty errors. There are some platforms where pointers and ints are
returned differently, for example in different processor registers.
And there are 64-bit platforms today, and more of them all the time,
where int and pointers are different sizes, 32 bits for int and 64
bits for pointers.

With the prototype and without the cast, the compiler will happily
take the irrelevant value in the integer return register, convert it
to a pointer, and store that, completely ignoring the real pointer
value in the proper register. Or if ints and pointers are returned in
the same way, it will happily throw away the top 32 bits of the real
64-bit pointer, then zero extend or sign extend the lower 32 bits into
64. The most likely result is a seg fault when the pointer is
dereferenced.

There have been a large number of platforms at various times where int
and pointer happened to have the same size. This was very common in
the early days of 8 and 16-bit processors, and on today's 32-bit
desktops (Windows, *NIX, etc.). The majority of these platforms also
happen to return ints and pointers in the same register, so this sort
of sloppy programming "works by accident" a large part of the time on
many platforms. But it has never been correct C, and there have
always been platforms where it doesn't work. With the ongoing
transition from 32 to 64 bits on the desktop, there will be a lot more
platforms in the future where it doesn't work by accident.
memset(path, 0, BUF_LENGTH);


First, you don't need to set the entire buffer to 0 for
what you are doing. Second, if you do need to allocate
memory for an array of characters and have it all zeroed,
calloc() will do both operations in one call. Note that
calloc() is guaranteed to initialize an array of
characters to all valid '\0' values, it is not so
guaranteed to do so with any other type.


calloc()? I tried 'man calloc' on my linux system, but it didn't find the
man page.


Then your man pages are deficient. The calloc() function has part of
the C library since before the original K&R boot (1978) and part of
every C standard since the original 1989 ANSI version. Get a better
reference.
But all you really need is one '\0' at the beginning, so
you could just code:

*path = '\0';
/* The length of the file path should be the
difference between the * location of file pointer
and the string pointer. I can't always assume
* this, right? */
int length_of_file_ path = file_name - string;


The length of the string prior to the final '/',
excluding the final '/' but allowing for a '\0' in its
place to terminate it, is exactly file_name - string. It
can't ever be anything else (if strrchr() does not return
NULL). You can always assume this.


Ok. I was wondering what would happen if the character array was not stored
in one contigous block in memory (i.e. if it were really long). The
subtracting the two addresses of the pointers would not give me the correct
length.


No matter how long they are, if they exist they are contiguous.
Arrays are always contiguous, as are the bytes allocated by malloc,
calloc, or realloc. Your operating system might provide virtual
memory, so they might be scattered all over physical memory or even
partially in a swap file, but that is all transparent to a C program.
To the program they are always contiguous and always will be.
/* Put the first length_of_file_ path characters into
path */ strncat(path, string, length_of_file_ path);


The problem with this is length_of_file_ path includes the
final '/', which you do not want to appear in the path
string (or do you?). If you don't, use
length_of_file_ path - 1 as the length argument.


I did want the final '/' to be included.


OK, but that is not how your original post read.
printf("The file path is %s\n", path);

}


Thanks for your help!


You're welcome.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.l earn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
17488
by: Glenn A. Harlan | last post by:
Why am I receiving the below error when calling - Path.GetTempFileName() The directory name is invalid. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.IO.IOException: The directory name is invalid.
39
2650
by: Joe Laughlin | last post by:
If I have a character array with "/some/file/directory/file_name", are there any functions / libraries that I could use to separate the directory ("some/file/directory") from the file name ("file_name"). I looked at sscanf(), but that didn't seem to do what I wanted. Thanks, Joe
9
6466
by: Benny Ng | last post by:
Hi,all, How to let the sub-directory to avoid the authentication control from Root's webconfig? I heard that we can add a new web.config to the sub-directory. And then we can slove the problem. Virtual directory is £ºhttp://localhost/main Sub-directory is : http://localhost/main/reminder
4
3338
by: tshad | last post by:
I have a site www.stf.com and a site www.stfstage.com (where I do all my testing). The problem is that www.stfstage.com is only internal and I need to get access from the outside (without creating a new domain). I tried to create a Virtual directory inside my stf site so that I would access it like: www.stf.com/stage/. I run as www.stfstage.com fine and have for a long time.
0
8838
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9577
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
9396
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
9339
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
6081
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4887
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3322
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
2804
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2225
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.