How would i get a file name from a path?? I didnt see an API call for it and the only thing i thought of is a loop from end to start and when i find the first \ shift all charactors but i couldnt get that working.
That's pretty much the way to do it without using API (like you, I don't know of one either). Walk the string backwards stuffing each character into a buffer until you hit '\' at which point you know you are finished.
If you post some code, we can help you better.
Tim
PathRemoveFileSpec (http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/shlwapi/path/pathremovefilespec.asp?frame=true) from shlwapi.dll will do it.
Cheers,
Zooba :U
hmmm just as i figured it out.. Thx tho.. Also here is what i came up with. Would you recommend one over the other?
GetFileNameFromPath PROC SRC:DWORD, DST:DWORD
LOCAL ln:DWORD
LOCAL lnst:DWORD
push edi
INVOKE StrLen,SRC
mov lnst, eax
mov ln, 0
mov edx, SRC
mov esi, lnst
@@:
sub esi, 1
add ln, 1
mov al, [edx+esi]
cmp al,"\"
jnz @B
sub ln, 1
mov eax, lnst
sub eax, ln
add edx, eax
mov edi, DST
xor ecx, ecx
@@:
mov al, [edx+ecx]
mov [edi+ecx], al
add ecx, 1
test al, al
jne @B
pop edi
mov eax, DST
ret
GetFileNameFromPath endp
also PathFindFileName looks more like what i would want since i want the file name.
Hehe, good point. Guess I didn't read the original question properly... :red
Probably a better solution is to simply point to the start of the filename. Find the last '\' like you have been and then pass the address of the next byte to whatever function you're using. This will save shifting it all along.
Keep in mind that you can't pass this value to a free memory function :U
so more like this or did i misunderstand what you said??
GetFileNameFromPath PROC SRC:DWORD, DST:DWORD
push edi
mov ecx, SRC
@@:
mov al, [ecx]
add ecx, 1
test al, al
je @F
cmp al, "\"
jne @B
mov edx, ecx
jmp @B
@@:
mov edi, DST
xor ecx, ecx
@@:
mov al, [edx+ecx]
mov [edi+ecx], al
add ecx, 1
test al, al
jne @B
pop edi
mov eax, DST
ret
GetFileNameFromPath endp
jckl,
This is what I cooked up. It takes the address of the path string and returns the address of the first byte after the last '\':
Path2FileName proc lpSrc:dword
push edx
mov edx, lpSrc ;edx = starting address of the string to process
invoke lstrlen, lpSrc
add edx, eax ;edx = address of the last byte of the string
findbackslash:
mov al, [edx]
cmp al, '\' ;is it a backslash?
je backslashfound
dec edx ;keep walking backwards
jmp findbackslash
backslashfound:
inc edx ;move forward a character as we were last pointing to a '\'
mov eax, edx ;return the address of the byte we want.
pop edx
ret
Path2FileName endp
See if it meets your requirements. It doesn't produce a new string, but makes your existing string usable for your purpose.
Regards,
Tim
well i need to keep the old one with the full path aswell so that wouldnt work. I need it to be put in a new string so i have both. Thanks tho :bg
jckl,
It doesn't alter the original string. It only yields the portion of the full path that represents the file name without the path. This way does yield both.
Regards,
Tim
Quote from: jckl
findbackslash:
mov al, [edx
cmp al, '\' ;is it a backslash?
je backslashfound
dec edx ;keep walking backwards
jmp findbackslash
technically u got a bug there, if the string doesnt have a backslash in it (and i have seen some return with / instead of \) your code will most likely screw up
best check that edx >= start of string :)
Quote from: jckl on March 15, 2006, 03:03:16 AM
How would i get a file name from a path?? I didnt see an API call for it and the only thing i thought of is a loop from end to start and when i find the first \ shift all charactors but i couldnt get that working.
Here some code that should be of value.
[attachment deleted by admin]
From masm32 in the m32lib folder - namefpth.asm
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
align 4
NameFromPath proc src:DWORD,dst:DWORD
push esi
push edi
mov esi, src
mov ecx, esi
mov edx, -1
xor eax, eax
@@:
add edx, 1
cmp BYTE PTR [esi+edx], 0 ; test for terminator
je @F
cmp BYTE PTR [esi+edx], "\" ; test for "\"
jne @B
mov ecx, edx
jmp @B
@@:
cmp ecx, esi ; test if ECX has been modified
je error ; exit on error if it is the same
lea ecx, [ecx+esi+1] ; add ESI to ECX and increment to following character
mov edi, dst ; load destination address
mov edx, -1
@@:
add edx, 1
mov al, [ecx+edx] ; copy file name to destination
mov [edi+edx], al
test al, al ; test for written terminator
jnz @B
sub eax, eax ; return value zero on success
jmp nfpout
error:
mov eax, -1 ; invalid path no "\"
nfpout:
pop edi
pop esi
ret
NameFromPath endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end
Paul
There is also the CRT _splitpath (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__splitpath.2c_._wsplitpath.asp) function.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
; These from stdlib.h:
_MAX_PATH equ 260 ; max. length of full pathname
_MAX_DRIVE equ 3 ; max. length of drive component
_MAX_DIR equ 256 ; max. length of path component
_MAX_FNAME equ 256 ; max. length of file name component
_MAX_EXT equ 256 ; max. length of extension component
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
modpath db _MAX_PATH dup(0)
moddrive db _MAX_DRIVE dup(0)
moddir db _MAX_DIR dup(0)
modfname db _MAX_FNAME dup(0)
modext db _MAX_EXT dup(0)
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
invoke GetModuleFileName, 0, ADDR modpath, _MAX_PATH
print ADDR modpath,13,10
invoke crt__splitpath,ADDR modpath,
ADDR moddrive,
ADDR moddir,
ADDR modfname,
ADDR modext
print ADDR moddrive, 13,10
print ADDR moddir, 13,10
print ADDR modfname, 13,10
print ADDR modext, 13,10
inkey "Press any key to exit..."
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
Why not a bit faster.. :lol
OPTION PROLOGUE:NONE ; turn it off
OPTION EPILOGUE:NONE
FileNameFromPath proc lpPath:DWORD
mov eax, [esp+4] ; eax->lpPath
mov [esp+4], ebx ; saving ebx register
mov ebx, [eax] ;
mov ecx, eax ; eax->lpPath
@LV1:
lea edx, [ebx-1010101h]
xor ebx, 5C5C5C5Ch ; 5Ch -> ASCII code of "\"
add ecx, 4
sub ebx, 1010101h ;
or edx, ebx ; testing "\" and 0 simultaneously
mov ebx, [ecx] ; ebx-> next dword
and edx, 80808080h ;
je @LV1
cmp byte ptr [ecx-4], 5Ch ; 5Ch -> ASCII code of "\"
mov edx, -4
jne @LV4
@LV2:
lea eax, [ecx+edx+1] ; eax->address of next char after "\"
@LV3:
add edx, 1
je @LV1
cmp byte ptr [ecx+edx], 5Ch ; 5Ch -> ASCII code of "\"
je @LV2
@LV4:
cmp byte ptr [ecx+edx], 0 ; is it end of string?
jne @LV3
mov ebx, [esp+4] ; restore ebx register
ret 4
FileNameFromPath endp
OPTION PROLOGUE:PROLOGUEDEF ; turn back on the defaults
OPTION EPILOGUE:EPILOGUEDEF
Regards,
Lingo
crt__splitpath, that is amazing! :U
Paul
From MASM32.lib, there is NameFromPath.
The source is available for learning purposes.
Regards, P1 :8)
P1
I just said that in the posting before MichaelW's and I included the code.
Paul
crt__splitpath, never noticed that before. Handy. :thumbu
Quote from: PBrennick on March 15, 2006, 03:44:57 PMI just said that in the posting before MichaelW's and I included the code.
Paul, I flew by all the posted code, seeing I would have just used the library function.
Plus, I keep a check on the Forum at work, while I wait for computers to finish working on different things.
Sorry, P1 :8)
Quote from: lingo on March 15, 2006, 02:55:09 PM
Why not a bit faster.. :lol
[snip...]
Now that's what I call
code! So, who has the decryption key? :toothy
Hi zooba,
Thanks for the info. Here is a simple demo :
.586
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\msvcrt.inc
include \masm32\include\shlwapi.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib
includelib \masm32\lib\shlwapi.lib
.data
pszPath db 'C:\masm32\bin\ml.exe',0
format1 db 'The path without file spec is : %s',0
.data?
buffer db 50 dup(?)
.code
start:
invoke PathRemoveFileSpec,ADDR pszPath
invoke crt_printf,ADDR format1,ADDR pszPath
invoke ExitProcess,0
END start
The usage of the high level garbage is a big shame for every ASSEMBLY programmer
Why garbage..
here is PathRemoveFileSpec proc from shlwapi.dll
BOOL __stdcall PathRemoveFileSpecA(LPSTR pszPath)
public PathRemoveFileSpecA
PathRemoveFileSpecA proc near
pszPath = dword ptr 8
mov edi, edi
push ebp
mov ebp, esp
push esi
push edi
mov edi, [ebp+pszPath]
test edi, edi
jz loc_77FA7285
cmp byte ptr [edi], 0
mov eax, edi
mov esi, edi
jz loc_77FA7254
loc_77FA722B:
mov cl, [eax]
cmp cl, 5Ch
jnz loc_77FA7236
mov esi, eax
jmp loc_77FA7248
loc_77FA7236:
cmp cl, 3Ah
jnz loc_77FA7248
lea ecx, [eax+1]
cmp byte ptr [ecx], 5Ch
jnz loc_77FA7245
mov eax, ecx
loc_77FA7245:
lea esi, [eax+1]
loc_77FA7248:
push eax ; lpsz
call CharNextA
cmp byte ptr [eax], 0
jnz loc_77FA722B
loc_77FA7254:
mov al, [esi]
test al, al
jz loc_77FA7285
cmp esi, edi
jnz loc_77FA7262
cmp al, 5Ch
jz loc_77FA7271
loc_77FA7262:
lea ecx, [edi+1]
cmp esi, ecx
jnz loc_77FA727D
cmp al, 5Ch
jnz loc_77FA727D
cmp [edi], al
jnz loc_77FA727D
loc_77FA7271:
xor eax, eax
cmp [esi+1], al
jz loc_77FA7287
mov [esi+1], al
jmp loc_77FA7282
loc_77FA727D:
mov byte ptr [esi], 0
xor eax, eax
loc_77FA7282:
inc eax
jmp loc_77FA7287
loc_77FA7285:
xor eax, eax
loc_77FA7287:
pop edi
pop esi
pop ebp
retn 4
PathRemoveFileSpecA endp
lingo,
QuoteWhy garbage..
here is PathRemoveFileSpec proc from shlwapi.dll
Vortex helps everybody and has contributed a lot of tools to this forum and to the community. I believe that you owe him an apology. Your hurtful words were unnecessary and this thread was a harmonious bringing together of different people with different ideas. You are a discordant. I hope you do not intend to continue with old habits.
If you wish to discuss something, what about your code? You did NOT write that. It is from a dissassembly. You did not even bother to take the effort to hide the fact. At least Erol's code was written by Erol
Please be civil,
Paul
"You can program so there is no reason why you cannot do those tests yourself."
"Good, you have a lot of people to ask then, you had better get busy. "
from www.masmforum.com/simple/index.php?topic=4071.msg30578#msg30578
again people who cannot control themselves try to poison the thread
Hey guys,
try and keep this friendly. If you look at how uncritical the task is of chopping the file name off the end of a path, the real contest is how to do it even bigger and slower so it looks like it has been written in C++. :bg
If I suffer severe brain rot later in the day, I may try a reverse optimisation to try and emulate C++ style performance.
Hi Hutch,
So this means you do not have a problem with him calling Vortex's code Garbage?
Paul
This would be my shorter code without relying on any external function. It returns the starting address of the file name in EAX. Unless you have a very specific reason to have a duplicate copy of such a file name, its address should be sufficient in the majority of cases.
NameFromPath proc USES edi src:DWORD
mov edi,src
xor eax,eax
mov ecx,-1
repnz scasb ;find terminating 0
dec edi ;return pointer to the 0 byte
neg ecx
dec ecx ;ECX = length of path+filename (including terminating 0)
;you could always save this for later use if needed
mov al,"\"
std
repnz scasb
cld ;EDI points to byte preceeding either "\" or start of filename
jnz @F ;no "\" found
inc edi ;EDI now points to "\"
; inc ecx ;adjust if you need to know the length of the path
@@:
inc edi ;EDI now points to start of filename
mov eax,edi
ret
NameFromPath endp
If the total length of the path+filename is saved, the count remaining in the ECX register after backscaning for the "\" could be used to determine the length of the "clean" filename. If you really need to make a duplicate copy, you could use such count with "movsb", or simply copy it byte by byte until you see the terminating 0.
That's the beauty of assembly, so many ways of doing the same thing. :clap: :clap:
Raymond
Quotetechnically u got a bug there, if the string doesnt have a backslash in it (and i have seen some return with / instead of \) your code will most likely screw up
best check that edx >= start of string :)
Thanks for pointing this out. This had ocurred to me after I posted it, and infact I had it in the back of my mind that I would need ecx somewhere within the body of the procedure but alas it slipped my mind. The way it stands I would cause an access violation by walking backwards to god-knows-where when passed a string without a backslash in it.
Other newcomers: Pay attention to this. This is how "works most of the time" assembler code gets created. :lol
Tim
I use the full path aswell as teh file name alone so i would need both which is why i saved the file name to a new string. Thx for all the different ideas though. This helps me go back and look at the different methods and i can learn more from this. I think i am doing pretty well for starting MASM less than 1 month ago :U
Quote from: jckl on March 16, 2006, 05:29:01 AM
I use the full path aswell as teh file name alone so i would need both which is why i saved the file name to a new string. Thx for all the different ideas though. This helps me go back and look at the different methods and i can learn more from this. I think i am doing pretty well for starting MASM less than 1 month ago :U
You only need to create a new string if you intend to modify them. The string quite literally starts from where the pointer points to and ends at the first null-character. For example:
.data
szString "This is my string", 0
.code
Main:
; The PrintString macro takes the address of a
; string and displays it on the console.
Print OFFSET szString ; displays "This is my string"
lea esi, szString
Print esi ; displays "This is my string"
add esi, 5
Print esi ; displays "is my string"
mov edi, OFFSET szString
Print edi ; displays "This is my string"
Print esi ; displays "is my string"
As you can see, after we add 5 to esi it still points to a string. If we modify the string pointed to by esi the string pointed to by edi will also be modified, since it is the same string. So as you can see, unless you plan to modify the file name without changing the full path, you can find the last backslash in the full path and from there on use that address (plus one) to use the filename.
Also, if you want to separate the path from the name, you can replace the last '\' with a null character. That way, the original pointer now points at the path without the filename (or trailing backslash) and you can store a pointer to the file name. :bg
Hi Ray, :wink
Thanks for sharing!
Here is your short code in memory:
00401677 55 push ebp
00401678 8B EC mov ebp,esp
0040167A 57 push edi
0040167B 8B 7D 08 mov edi,dword ptr [ebp+8]
0040167E 33 C0 xor eax,eax
00401680 B9 FF FF FF FF mov ecx,0FFFFFFFFh
00401685 F2 AE repne scas byte ptr [edi]
00401687 4F dec edi
00401688 F7 D9 neg ecx
0040168A 49 dec ecx
0040168B B0 5C mov al,5Ch
0040168D FD std
0040168E F2 AE repne scas byte ptr [edi]
00401690 FC cld
00401691 75 01 jne 00401694
00401693 47 inc edi
00401694 47 inc edi
00401695 8B C7 mov eax,edi
00401697 5F pop edi
00401698 C9 leave
00401699 C2 04 00 ret 4 ;total: 37 bytes
If you rewrite your code in this way it will be 6 bytes shorter...
OPTION PROLOGUE:NONE; turn it off
OPTION EPILOGUE:NONE
NameFromPath proc src:DWORD
pop eax ; return address
pop ecx ; ecx->src
push eax
push edi
mov edi, ecx
xor eax,eax
or ecx, -1 ;mov ecx,-1 is 5 bytes long->or ecx,-1 is 3 bytes
repnz scasb
dec edi
neg ecx
dec ecx
mov al, "\"
std
repnz scasb
cld
jne @f
inc edi
@@:
inc edi
mov eax,edi
pop edi
ret
NameFromPath endp
OPTION PROLOGUE:PROLOGUEDEF; turn back on the defaults
OPTION EPILOGUE:EPILOGUEDEF
in memory:
0040169C 58 pop eax
0040169D 59 pop ecx
0040169E 50 push eax
0040169F 57 push edi
004016A0 8B F9 mov edi,ecx
004016A2 33 C0 xor eax,eax
004016A4 83 C9 FF or ecx,0FFFFFFFFh
004016A7 F2 AE repne scas byte ptr [edi]
004016A9 4F dec edi
004016AA F7 D9 neg ecx
004016AC 49 dec ecx
004016AD B0 5C mov al,5Ch
004016AF FD std
004016B0 F2 AE repne scas byte ptr [edi]
004016B2 FC cld
004016B3 75 01 jne 004016B6
004016B5 47 inc edi
004016B6 47 inc edi
004016B7 8B C7 mov eax,edi
004016B9 5F pop edi
004016BA C3 ret ; total: 31 bytes
Regards,
Lingo
hmmmm,
Quote
Hi Hutch,
So this means you do not have a problem with him calling Vortex's code Garbage?
No, I have a problem with anyone who tries to start an argument in the forum and this is why I ask members if comments get a bit heated to give it a rest and try and keep things friendly. This forum is not the Bureau of Morality and it will not be used that way by anyone.
QuoteThe usage of the high level garbage is a big shame for every ASSEMBLY programmer
lingo,
To each his own.
I prefer the high-level MASM syntax. It's easier to write, easier to read, easier to maintain and is more productive. I also don't hesitate calling C run-time functions from assembly if they fit the need. I also program in C, PowerBASIC and even C# [gasp]. You have a right to your "assembly extremist" view, but I would say that most of the people in this forum don't agree with that view.
:P
lingo,
You can be sure that M$'s bloated code is much more successfull than your little optimized codes, they make a lot of money with that bloated code you don't like :bdg
it may be required that my filename changes so again my solution was my best choice for my situation. The thing is i am adding files to a pff file which is kind of like a zip file. The pff file holds many other files and i do not want duplicate names so i may need to change the file name alone without changing the path when the file is added if there is already a file with that same name.
as i said thanks for all the different ideas and such, just this fits my solution the best i know of :toothy
Hi Greg, :lol
" It's easier to write, easier to read, easier to maintain and is more productive."
It is not my business, so you can use what you want...
It is just my opinion and I don't want to offend anybody with it
When I talk about garbage it is a garbage ( I proofed it) and for it is guilty the
stupid compiler, not the high level language programmers
On the other hand it doesn't matter who and why use functions with garbage in them;
Hutch, me , Vortex, you or somebody else...
I will respect all the people again but the final exe file will be non "garbage" free...
Obviously this topic is ready for The Colosseum.... :lol
"..but I would say that most of the people in this forum don't agree with that view.
I don't believe that you have a right to talk from the others' people name
"I also don't hesitate calling C run-time functions from assembly if they fit the need.
I prefer to read ""I also don't hesitate calling m32lib functions if they fit the need"..he..he.. :lol
"I also program in C, PowerBASIC and even C#
Some years ago I earned a good money with C/C++ too... :lol
Vortex,
"You can be sure that M$'s bloated code is much more successfull than your little optimized codes, they make a lot of money with that bloated code you don't like
Of course I'm sure but would you be so kind to tell me what is your connection
with others' money...It is very strange argument for me
On the other hand Intel created better C/C++ compiler with more optimized functions and with
usage of the new registers... but it is other story
Regards,
Lingo
I am not forced to have any connections. Also, a lot of newspapers and magazines, they are not supposed to have connections with M$ when they are talking about M$'s yearly income.
Hutch,
Like me, Paul wanted just to support the API function to extract the file name. The individual who disassemled the API function thinks that it's bloatware. I wonder if it's allowed to post here the disassembly output of M$'s copyrighted code.
I know all of you guys and these things are just a matter of personal choice, Lingo is probably right that a hand rolled version will be smaller and faster bit it is equally as valid to say "who cares" when its hardly a speed critical task. In fact most operating system code is well off the pace in speed terms but many use various bits of it to do the job.
I don't want to have to split this thread into bits so all I have asked is that it be kept friendly so that no-one ends up offended.
Quote from: Timbo on March 15, 2006, 03:18:24 AM
That's pretty much the way to do it without using API (like you, I don't know of one either). Walk the string backwards stuffing each character into a buffer until you hit '\' at which point you know you are finished.
It shouldn't be forgotten that
D:filename.ext is a valid path, too. I always scan the string backwards until any of the folllowing character is encountered:
slash, backslash, colon.
Two things, in Windows most file name operations will fail with a path that is a drive letter and a colon followed by a file name. It was a valid path on FCB dos 1 technology but most Windows versions from 3.0 upwards require at least a backslash and with later versions, they will often handle a unix style forwards slash as well, mainly for network paths.
As far asa the technique, scanning a string backwards is a poor technique and it is easier to scan it forward and store the last location of a slash(forward or back) or a colon if there is one. If there is none of these characters it may just be a bare filename.
well i am getting the path from the GetOpenFileName api call so i would assume it to always have a \ but i am not sure on that.