Go Back   Winamp Forums > Developer Center > NSIS Discussion

Reply
Thread Tools Search this Thread Display Modes
Old 15th July 2003, 16:52   #1
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
Copying single multibyte characters with StrCpy

I have this function that takes a string that is a directory path and tries to figure out what's the upper most directory that has to be created.

For example, if the user wants to install the app to:
C:\Program Files\ABC\123

and ABC doesn't exist in Program Files, the function will return C:\Program Files\ABC, it makes it usefull for deleting all the directories that you've created upon uninstall.

The function looks like:

PHP Code:
  FindTopLevelDirectory
  
inputnoneuses $INSTDIR
  
outputtop of stack (replaceswith e.gC:Program Files)
  ; 
the top-most parent of $INSTDIR that does not currently exist
  
modifies no other variables.
  ;
  ; 
Usage:
  ;   
Call FindTopLevelDirectory
  
;   Pop $R0
  
;   ; at this point $R0 will equal "C:\Program Files\NonexistantDirectory"
  
function FindTopLevelDirectory
    
$R2 directory path to installation
    
$R3 counter
    
$R4 temp counter
    
$R5 temp string
    Pop $R2 
;the orginal string
    StrCpy $R3 
"-1"
    
StrCpy $R5 ""
    
Loop:
    
IntOp $R3 $R3 1
    StrCpy $R4 $R2 1 $R3
    StrCmp $R4 
"" ExitLoop
    StrCmp $R4 
"\" Directory
    StrCpy $R5 $R5$R4
    Goto Loop
    Directory:
    StrCpy $R5 "
$R5"
    IfFileExists "
$R5*.*" "" ExitLoop
    StrCpy $R5 "
$R5"
    Goto Loop
    ExitLoop:
    IfFileExists "
$R5*.*" +2
    Push $R5
  FunctionEnd 
The problem is when the path has Japanese characters, the statement
PHP Code:
StrCpy $R4 $R2 1 $R3 
returns an invalid character, which is expected, because it's a multibyte character. I somtimes can detect this by doing the following instead:

PHP Code:
         StrCpy $R4 $R2 1 $R3
         StrCmp $R4 
"" "" Check
    
; try 2 bytes in case its a multibyte folder.
         
StrCpy $R4 $R2 2 $R3 
    StrCmp $R4 
"" ExitLoop
    Check
:
         
StrCmp $R4 "\" Directory 
But sometimes the copying 1 character returns a valid character, even though it's part of a multibyte character, and I get what is commonly known as mojibake.

Does anyone have any ideas of a better way to copy one character at a time that handles multibyte characters? Or is there a better way to iterate over the parent directories of a path?

I'm using NSIS 2.01b if that helps.
tderouin is offline   Reply With Quote
Old 15th July 2003, 16:56   #2
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
Use GetParent to get the C:\Program Files\ABC bit, then use GetFileName to get hold of ABC.

All functions are on the NSIS Archive

-Stu
Afrow UK is offline   Reply With Quote
Old 15th July 2003, 16:59   #3
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
That has the same problem.

PHP Code:
StrCpy $R2 $R0 1 $R1 
tderouin is offline   Reply With Quote
Old 15th July 2003, 17:02   #4
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
Have you tried getting the latest CVS, because maybe new support for these characters have been added.
I know that it will be a lot of work converting to the new NSIS2b4 format, but it's worth it.

-Stu
Afrow UK is offline   Reply With Quote
Old 15th July 2003, 17:19   #5
kichik
M.I.A.
[NSIS Dev, Mod]
 
kichik's Avatar
 
Join Date: Oct 2001
Location: Israel
Posts: 11,310
The latest CVS version won't help with this. But it does contain an example to what you're trying to do (folder stuff, not MBCS). You can find this example online, here.

To be to get one "real" character you should use CharNext with System.dll.

BTW, why are you comparing it to a quoted space?

NSIS FAQ | NSIS Home Page | Donate $
"I hear and I forget. I see and I remember. I do and I understand." -- Confucius
kichik is offline   Reply With Quote
Old 15th July 2003, 17:39   #6
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
With the GetParent and GetFileName functions, just blank out the lines that compare the variable to "" that come below the single character seperation.
It won't matter whether or not the character is invalid ("") or not then.

(All you want to look for is "\", so there is no need to check for "" which is usually the output got at the end of a string)

-Stu
Afrow UK is offline   Reply With Quote
Old 15th July 2003, 23:50   #7
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
I'm not comparing anything to a quoted space. It's an empty string. ""
tderouin is offline   Reply With Quote
Old 15th July 2003, 23:57   #8
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
So if i blank those lines, how does it know if it's reached the end of the string?
tderouin is offline   Reply With Quote
Old 16th July 2003, 08:48   #9
kichik
M.I.A.
[NSIS Dev, Mod]
 
kichik's Avatar
 
Join Date: Oct 2001
Location: Israel
Posts: 11,310
Haha, I'm going blind

Anyway, the only way you'd know what you're up against is by using CharNext or IsDBCSLeadByte. But you don't need either, just use the script I have attached, it's much more efficient.

NSIS FAQ | NSIS Home Page | Donate $
"I hear and I forget. I see and I remember. I do and I understand." -- Confucius
kichik is offline   Reply With Quote
Old 16th July 2003, 15:14   #10
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
Right, I see why that solution would work. There's a problem with it however, if the user had created an empty directory previously, then installed the application into that directory, the uninstall would delete the directory the user created beforehand even though it wasn't created by the application's installer.

This may or may not be the wish of the user. The safer route is to delete only the directories that the installer creates.
tderouin is offline   Reply With Quote
Old 16th July 2003, 15:42   #11
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
Quote:
Originally posted by tderouin
So if i blank those lines, how does it know if it's reached the end of the string?
As long as the string has "\" in it then it won't need to go to the end of the string

-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 15:43   #12
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
$INSTDIR isn't terminated with a \ character.
tderouin is offline   Reply With Quote
Old 16th July 2003, 15:46   #13
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
?
If $INSTDIR is e.g. C:\quake2\dday, then it will see the "\" before "dday".

$INSTDIR is a run-time variable, so changes to whatever the user has entered.

-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 15:49   #14
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
Right.
What about this example: C:\Program Files\ABC (C:\Program Files exists, ABC does not)

It checks to C:\Program Files\ finds it exists.
Keeps copying over characters. Copies A, then B, then C, then copies "" not "\" and just loops infinitely because we're not checking for the end of a string that's not terminated by "\".

Doesn't work, does it?
tderouin is offline   Reply With Quote
Old 16th July 2003, 15:58   #15
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
I don't understand why you think it won't work.
It checks for "\", then when it is found (it will be) it gets straight out of the loop, gets ABC from the string, then exits the function.
There is no way it will loop unless you're string has no "\" in it.

-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 16:02   #16
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
I know it won't work because I tested it. Take a look at the loop:

PHP Code:
    Loop:
    
IntOp $R3 $R3 1
    StrCpy $R4 $R2 1 $R3
    StrCmp $R4 
"" ExitLoop
    StrCmp $R4 
"\" Directory
    StrCpy $R5 $R5$R4
    Goto Loop 
It loops copying over a character at time. When it has copied over the final C, it has nothing left to copy, so $R4 is "" on the next iteration. Since it's not terminated by "\", it continues to loop, $R4 continues to be "". Taking that statement out will cause it to loop infinitely if the string is not terminated by a back slash, guaranteed.
tderouin is offline   Reply With Quote
Old 16th July 2003, 16:04   #17
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
I am wondering now why you need the function...
Because, the $INSTDIR is created automatically anyway.

If $INSTDIR is C:\progra~1\abc\123 then subdirs abc and 123 are created anyway.

-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 16:05   #18
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
Code is wrong.
code:

StrCpy $R3 0
Loop:
IntOp $R3 $R3 - 1
StrCpy $R4 $R2 1 $R3
StrCmp $R4 "" ExitLoop
StrCmp $R4 "\" Directory
StrCpy $R5 $R5$R4
Goto Loop



-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 16:06   #19
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
Right. But if you read my previous posts you'll see I'm worried about the uninstall process.

RMDIR $INSTDIR

only removes C:\progra~1\abc\123 and not C:\progra~1\abc.
tderouin is offline   Reply With Quote
Old 16th July 2003, 16:09   #20
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
There's a problem with what you want too...
What happens if the $INSTDIR is just C:\progra~1\abc?
abc will be removed, then you will remove the next directory down which is C:\progra~1!!!

-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 16:11   #21
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
Are you that familiar with the NSIS code?

RMDIR removes the directory only if it's empty.

RMDIR /R removes the directory recursively.

In the previous code C:\Program Files\abc would be identified as the top level directory and stored in the registry. That would be the top most directory that would be removed, not C:\Program Files, not sure where you got the idea that the code would attempt to remove that directory.
tderouin is offline   Reply With Quote
Old 16th July 2003, 16:18   #22
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
Sorry, missed that.

Ok. I will right a small function for this because I think that it would be useful.

-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 16:18   #23
kichik
M.I.A.
[NSIS Dev, Mod]
 
kichik's Avatar
 
Join Date: Oct 2001
Location: Israel
Posts: 11,310
There is a very simple solution to avoid comparing to "" and it's to simply append a back-slash to the end of the string.

As for deleting empty directories the user have created, unless you want to create the directories manually at install time and save in a log each directory created, that's what you've got.

NSIS FAQ | NSIS Home Page | Donate $
"I hear and I forget. I see and I remember. I do and I understand." -- Confucius
kichik is offline   Reply With Quote
Old 16th July 2003, 16:29   #24
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
Try this out.

Usage:
Push 2 ;check 2 last dirs
Push $INSTDIR
Call RemoveDirs

code:

Function RemoveDirs
Exch $R0 ;input string
Exch
Exch $R1 ;maximum number of dirs to check for
Push $R2
Push $R3
Push $R4
Push $R5
IfFileExists "$R0\*.*" 0 +2
RMDir "$R0"
StrCpy $R5 0
top:
StrCpy $R2 0
StrLen $R4 $R0
loop:
IntOp $R2 $R2 + 1
StrCpy $R3 $R0 1 -$R2
StrCmp $R2 $R4 exit
StrCmp $R3 "\" 0 loop
StrCpy $R0 $R0 -$R2
IfFileExists "$R0\*.*" 0 +2
RMDir "$R0"
IntOp $R5 $R5 + 1
StrCmp $R5 $R1 exit
Goto top
exit:
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Pop $R0
FunctionEnd



-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 16:34   #25
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
Just tested it, and changed 1 thing - works fine.
Re-copy the script.
I will put this on the archive.

-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 16:38   #26
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
How are you calling it?
tderouin is offline   Reply With Quote
Old 16th July 2003, 16:50   #27
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
http://nsis.sourceforge.net/archive/...ances=0,11,211

-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 16:56   #28
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
That looks good. I'll give it a try.

However, how do you know how many directories are created? Would it involve iterating over $INSTDIR in SecCopyUI and create each directory piecemeal correct?

Then we'd probably have to copy a character at a time, right? Which takes us back to the original problem. I have yet to try out NextChar, I'll post my findings when I am finished.

Thanks!
tderouin is offline   Reply With Quote
Old 16th July 2003, 17:04   #29
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
Look at my script.
It bypasses the problem that we had before.

By comparing the strlen with the minus (chop number)
Therefore, if the strlen and chop number are equal, then it has got to the end of the string.

If you want to find out how many subdirectories there are, use this:
code:

Function SubdirsAmount
Exch $R0 ;input string
Push $R1
Push $R2
Push $R3
Push $R4
StrCpy $R1 0
StrLen $R2 $R0
loop:
IntOp $R1 $R1 + 1
StrCpy $R3 $R0 1 -$R1
StrCmp $R1 $R2 exit
StrCmp $R3 "\" 0 loop
IntOp $R4 $R4 + 1
Goto loop
exit:
StrCpy $R0 $R4
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Exch $R0 ;output
FunctionEnd



-Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2003, 17:05   #30
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Shropshire, England
Posts: 6,887
So you're code should look like this:
code:

Push $INSTDIR
Call SubdirsAmount
Pop $R0

Push $R0
Push $INSTDIR
Call UninstallDirs



-Stu
Afrow UK is offline   Reply With Quote
Old 18th July 2003, 14:49   #31
kichik
M.I.A.
[NSIS Dev, Mod]
 
kichik's Avatar
 
Join Date: Oct 2001
Location: Israel
Posts: 11,310
tderouin, do the MBCS characters come right if you copy byte by byte? Afrow UK's code should help you test that.

NSIS FAQ | NSIS Home Page | Donate $
"I hear and I forget. I see and I remember. I do and I understand." -- Confucius
kichik is offline   Reply With Quote
Old 23rd July 2003, 23:26   #32
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
I'm just getting around to testing this now, SubdirsAmount doesn't actually record how many directories are going to be created. It should probably be changed to check to see if a directory exists, if it doesn't exist then increment the number of subdirs, if it doesn't exist don't increment but continue copying.
I'll play with it and post new code if I find a better way.

Ex:
SubdirsAmount C:\Documents and Settings\user\Start Menu\Programs\Product

returns 6

but only one dir is being created, so in the uninstall log you see:

rmdir C:\Documents and Settings\user\Start Menu\Programs\Product
rmdir C:\Documents and Settings\user\Start Menu\Programs
rmdir C:\Documents and Settings\user\Start Menu
rmdir C:\Documents and Settings\user
rmdir C:\Documents and Settings
rmdir C:

Which wouldn't look to pleasant to the user.
tderouin is offline   Reply With Quote
Old 23rd July 2003, 23:57   #33
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
I found this was a good way for finding the numbeof subdirs that had to be created:

PHP Code:
    Function SubdirsAmount
    Exch $R0 
;input string
    Push $R1
    Push $R2
    Push $R3
    Push $R4
    Push $R5
     StrCpy $R1 0
     StrLen $R2 $R0
    loop
:
     
IntOp $R1 $R1 1
      StrCpy $R5 $R0 
-$R1
      StrCpy $R3 $R0 1 
-$R1
     StrCmp $R1 $R2 
exit
     
StrCmp $R3 "\" 0 loop
     IfFileExists "
$R5*.*" loop ; don't increment if the directory exists
      IntOp $R4 $R4 + 1
    Goto loop
    exit:
    StrCpy $R0 $R4
    Pop $R5
    Pop $R4
    Pop $R3
    Pop $R2
    Pop $R1
    Exch $R0 ;output
    FunctionEnd 
So when installing I would do:
PHP Code:
   Push "$INSTDIR"
   
Call SubdirsAmount
   Pop $R0
   WriteRegStr HKCU 
"${REGISTRY_LOC}" "UninstallTopLevelFolder" "$R0" 
When uninstalling:
PHP Code:
  remove folders
  ReadRegStr $R0 HKCU 
"${REGISTRY_LOC}" "UninstallTopLevelFolder"
  
Push $R0
  Push 
"$INSTDIR"
  
call un.UninstallDirs 
tderouin is offline   Reply With Quote
Old 24th July 2003, 11:43   #34
kichik
M.I.A.
[NSIS Dev, Mod]
 
kichik's Avatar
 
Join Date: Oct 2001
Location: Israel
Posts: 11,310
What about the MBCS characters? Do they come out right?

NSIS FAQ | NSIS Home Page | Donate $
"I hear and I forget. I see and I remember. I do and I understand." -- Confucius
kichik is offline   Reply With Quote
Old 24th July 2003, 16:16   #35
tderouin
Member
 
Join Date: Jan 2003
Posts: 79
Yes, this seems to work for multibyte characters.
tderouin is offline   Reply With Quote
Old 13th August 2003, 20:08   #36
Joost Verburg
NSIS MUI Dev
 
Join Date: Nov 2001
Posts: 3,718
I have updated the GetParent function, so it should support MBCS now. Can you please test this one? Thanks
code:
Function GetParent
Exch $R0 ; old $R0 is on top of stack
Push $R1
Push $R2
Push $R3
StrLen $R3 $R0
loop:
IntOp $R1 $R1 - 1
IntCmp $R1 -$R3 exit exit
StrCpy $R2 $R0 1 $R1
StrCmp $R2 "\" exit
Goto loop
exit:
StrCpy $R0 $R0 $R1
Pop $R3
Pop $R2
Pop $R1
Exch $R0 ; put $R0 on top of stack, restore $R0 to original value
FunctionEnd

Joost Verburg is offline   Reply With Quote
Reply
Go Back   Winamp Forums > Developer Center > NSIS Discussion

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 22:25.