PATHS: trailing backslash or not trailing backslash? - vba

I've many VBA scripts and functions where I read a manually inserted (in Excel cell) local disk paths and I'm still confused about whether to normalize the inserted paths by adding or trimming the trailing backslash.
What do you use as the general standard? with or without the trailing backslash?
My confusion is fueled by the fact that for example ActiveWorkbook.path returns a path without trailing backslash while the CopyFile (FileSystemObject) method for the "Destination" parameter wants the trailing backslash otherwise it considers it as a file instead than a directory (and can give unexpected Permission denied errors)

The general idea is to always end a directory's name with a backslash. This is advised in the URL you mention, but there are quite some other situations as well (check my answer on a regularly occuring xcopy problem).
As mentioned: when you don't put a backslash, the question might arise "Is it a file or a directory?". Putting the backslash solves that question.
In top of that, while programming, regularly you might have following situation:
complete_filename = path + filename
Obviously, if you have forgotten to put the backslash at the end, this might cause problems (e.g. you don't want to create a file, called "C:\Tempoutput.txt" instead of "C:\Temp\output.txt", I presume? :-) )

Related

How do I write an SSIS Expression to extract one folder name from a fully qualified file name

I have an SSIS package with an ForEach File Enumerate loop (Fully Qualified Name) with an FTP task within in.
The package when executed will go through the files in the subfolders within the following directory
C:\Test\Test2\ABC\
*.txt
And it will post the files to an FTP site.
I have a defined variable called #[User::Filename] within the foreach loop.
But there are folders within the FTP and I want the files to go to based on the Folder they are taken from on the C drive.
C:\Test\Test2\ABC\A\1.txt
C:\Test\Test2\ABC\B\2.txt
C:\Test\Test2\ABC\C\3.txt
File 1.txt should go to the FTP folder Called \FTP\A
File 2.txt should go to the FTP folder Called \FTP\B
File 3.txt should go to the FTP folder Called \FTP\C
My original thought was to make the remote path a variable and piggy back off the the foreach loop variable Fully qualified name.
To do this I created a variable called #[User::FilenameFTP] and inputted the following into the expression
"//FTP//" +
RIGHT(
(LEFT(#[User::Filename], ABS((FINDSTRING(#[User::Filename], "//", 5)))),
ABS((FINDSTRING(#[User::Filename], "//", 5)-1)) - ABS((FINDSTRING(#[User::Filename], "//",4)+1))
)
I thought this formula would give me the filename in the C drive which the file is coming from and I used this as the Remote Path variable within the FTP task. But when I run it the files still go into \FTP\ and not into the subfolders.
I ran a script task on this and the output isnt showing what I want either. What am I doing wrong? Can this not be done this way editing the variable within the foreach loop?
If your drive names are coming in (more or less) as you have them shown, then those should be backslashes ("\\") instead of forward slashes in your expression. Might not be the issue, but I changed them to play around with this.
Using the C folder string, in the expression as written, ABS((FINDSTRING(#[User::Filename], "\\", 5)-1)) and ABS((FINDSTRING(#[User::Filename], "\\",4)+1)) both evaluate to 19, so the expression comes down to RIGHT(<<String>>,0), and, from the documentation, If integer_expression is zero, the function returns a zero-length string.. So you're not appending anything to the end of the FTP base folder name.
Down and Dirty Fix
We could probably mess around with all that LEFT and RIGHT and FINDSTRING, but if you know that the folder name you're after will always be the fifth element in your fully qualified name (which your expression is already dependent on) you can get there faster just using TOKEN, and specifying the fifth element of your slash-delimited string:
"//FTP//" + TOKEN( #[User::Filename],"\\",5) +"//"
Which evaluates to //FTP//C//.
More Sustainable Fix
On the other hand, if you want to future-proof your code a little, in anticipation of the day that you add or eliminate a level of folder hierarchy, I would suggest extracting the last folder name, without regard to how many levels of folder come first.
We can do that using SUBSTRING and some clever REVERSE work, with due credit to KeithL for this answer, that got me rolling.
SUBSTRING takes three arguments. We have our string #[User::Filename], so that's one. The second is the starting position from the left end of the string, and the third is the number of characters to extract.
To get the starting position, we'll find the position of the second to last slash using REVERSE to count characters from the right hand end of the string:
FINDSTRING(REVERSE( #[User::Filename]),"\\",2) (evaluates to 8 here)
So our starting position is the total length of the string, minus the number of characters back to the second to last slash.
LEN( #[User::Filename]) - FINDSTRING(REVERSE( #[User::Filename]),"\\",2) (=17)
We can get the number of characters to pull by subtracting the reversed position of the last slash from the reversed position of the second to last slash, then subtracting one more, since we don't want that trailing slash in our string yet.
FINDSTRING(REVERSE( #[User::Filename]),"\\",2)
- FINDSTRING(REVERSE( #[User::Filename]),"\\",1) - 1 (= 1 in our example)
And there are our three arguments. Putting those all together with your base folder name (and I added a trailing slash. If that doesn't work for you, take it out of there!):
"//FTP//"
+ SUBSTRING(
#[User::Filename] ,
LEN( #[User::Filename]) - FINDSTRING(REVERSE( #[User::Filename]),"\\",2),
FINDSTRING(REVERSE( #[User::Filename]),"\\",2)
-FINDSTRING(REVERSE( #[User::Filename]),"\\",1)-1 )
+ "//"
Evaluates to //FTP//C//.
Now, when the powers that be decide to "clean up" that source server, and the Test2 layer disappears all of a sudden, or things get crazy, and you bury this all one layer deeper, your code will still work.
Side Note
If you're really using drive letters in your file path names, like C:\, be aware that when you're running your packages locally, that's your C:\ drive the package is using, but when you deploy this, it'll be looking around on the server's C:\ drive, and it just might not like what it finds, or more likely, doesn't find there.

Renaming file via UNC path

I need to have my VB.NET program rename a file over the network.
Microsoft says that My.Computer.FileSystem.RenameFile does not work if the file path starts with two backslashes ("\\"). So, what other way is there of doing this? I just need to rename a file in the domain, for instance:
rename("\\domain\1\exemple.txt", "\\domain\1\exemple2.txt")
The second parameter for rename should be just the file name eg:
My.Computer.FileSystem.RenameFile("C:\Test.txt", "SecondTest.txt")
So try changing your code to this:
My.Computer.FileSystem.RenameFile(#"\\domain\1\exemple.txt", "exemple2.txt")
Also beware of escaping because \ is an escape character, so add a # before any string that contains \. This will cause it to ignore escaping and therefore will treat \ as a normal character

Using CMake's include_directories command with white spaces

I am using CMake to build my project and I have the following line:
include_directories(${LLVM_INCLUDE_DIRS})
which, after evaluating LLVM_INCLUDE_DIRS, evaluates to:
include_directories(C:\Program Files\LLVM\include)
The problem is that this is being considered two include directories, "C:\Program" and "Files\LLVM\include".
Any idea how can I solve this problem? I tried using quotation marks, but it didn't work.
EDIT: It turned out that the problem is in the file llvm-3.0\share\llvm\cmake\LLVMConfig.cmake. I enclosed the following paths with quotation marks and the problem was solved:
set(LLVM_INSTALL_PREFIX C:/Program Files/LLVM)
set(LLVM_INCLUDE_DIRS ${LLVM_INSTALL_PREFIX}/include)
set(LLVM_LIBRARY_DIRS ${LLVM_INSTALL_PREFIX}/lib)
In CMake,
whitespace is a list separator (like ;),
evaluating variable names basically replaces the variable name with its content and
\ is an escape character (to get the symbol, it needs to be escaped as well)
So, in your example, include_directories(C:\\Pogram Files\\LLVM\\include) is the same as
include_directories( C:\\Program;Files\\LLVM\\include)
that is, a list with two items. To avoid this, either
escape the whitespace as well:
include_directories( C:\\Program\ Files\\LLVM\\include) or
surround the path with quotation marks:
include_directories( "C:\\Program Files\\LLVM\\include")
Obviously, the second option is the better choice as it is
simpler and easier to read and
can be used with variable evaluation like in your example (since the result of the evaluation is then surrounded by quotation marks and thus, treated a single item)
include_directories("${LLVM_INCLUDE_DIRS}")
This works as well, if LLVM_INCLUDE_DIRS is a list of multiple directories because the items in this list will then be explicitly separated by ; so that there is no need for unquoted whitespace as implicit list item separator.
Side note:
When using hard-coded path-names (for whatever reason) in my CMake files, I usually uses forward slashes as directory separators as this works on Windows as well and avoids the need to escape all backslashes.
This is more likely to be an error at the point where LLVM_INCLUDE_DIRS is set rather than a problem with include_directories.
To check this, try calling include_directories("C:\\Program Files\\LLVM\\include") - it should work correctly.
The problem seems to be that LLVM_INCLUDE_DIRS was constructed without using quotation marks. Try for example running this:
set(LLVM_INCLUDE_DIRS C:\\Program Files\\LLVM\\include)
message("${LLVM_INCLUDE_DIRS}")
set(LLVM_INCLUDE_DIRS "C:\\Program Files\\LLVM\\include")
message("${LLVM_INCLUDE_DIRS}")
The output is:
C:\Program;Files\LLVM\include
C:\Program Files\LLVM\include
Note the semi-colon in the first output line. This is a list with 2 items.
So the way to fix this is to modify the way in which LLVM_INCLUDE_DIRS is created.

wix: does INSTALLDIR always end in a slash?

I think the default dir gets a trailing slash.
But what if the user selects a different directory?
Is there a way for the INSTALLDIR to NOT have a trailing slash?
It's not that I want it to not have a trailing slash. I want to know if I can count on it, so that, for example,
[INSTALLDIR]Filter.dll
...will always resolve to a real, valid filesystem path.
Currently I use
[INSTALLDIR]\Filter.dll
and I get a double-slash in there. It's valid and resolves, but I'd like to eliminate the double slash.
thanks.
Windows Instaler directory table entries ( which become properties after costing ) are always formatted by MSI to contain a trailing backslash. Including extra slashes will not harm the resolution.
It is my understanding that Windows Installer will add a backslash to any Directory table entry. Therefore, yes, you can eliminate your slash and not worry about things breaking.
In fact, this has even prompted InstallShield to create a KB article describing how to REMOVE the backslash...
http://kb.flexerasoftware.com/selfservice/viewContent.do?externalID=Q106587

Pound sign (#) in filename causing error

I have a very simple file upload that allows users to upload PDF files. On another page I then reference those files through an anchor tag. However, it seems that when a user upload a file that contains the pound sign (#) it breaks the anchor tag. It doesn't cause any type of Coldfusion error, it just can't find the file. If I remove the #, it works just fine. I am sure there are a number of other characters that would have this same issue.
I've tried putting URLEncodedFormat() around the file name inside the anchor but that doesn't help. The only other thing I could think of was to rename the file each time it was uploaded and remove the "#" character (and any other "bad" character).
There has got to be an easier solution. Any ideas?
If you control the file upload code try validating the string with
IsValid("url",usersFileName) or
IsValid("regex",usersFileName,"[a-zA-Z0-9]")
Otherwise if you are comfortable with regex I would suggest something like the previous posters are commenting on
REReplace(usersfilename,"[^a-zA-Z0-9]","","ALL")
These samples assume you will add the ".pdf" and only allows letters and numbers. If you need underscores or the period it would look like this...
REReplace(usersfilename,"[^a-zA-Z0-9\._]","","ALL")
I am not a regex guru, if I have one of these wrong I am sure several will jump in and correct me :)
Pound signs are not legal within filenames on the web. They are used for in-page anchor targets:
<a name="target">
So if you have file#name.pdf, the browser is actually looking for the file "file" and the internal anchor "name.pdf".
Yes, you will have to rename your files on upload.
I can't comment yet,
but Kevink's solution is good unless you need to perserve what you're replacing.
We ran into an instance where we needed to rename the filename but the filename needed to be somewhat preserved (user requirement). Simply removing special characters wasn't an option. As a result we had to handle each replace individually, something like.
<cfset newName = replace(thisFile, "##", "(pound)", "All")>
<cfset newName = replace(newName , "&", "(amp)", "All")>
<cffile action="rename"source = "#ExpandPath("\uploads\#thisFolder#\#thisFile#")#" destination = "#newName#">
Probably you would have to replace # with ## to avoid this, I think this is caused because # is figured as Coldfusion keyword.