Converting an absolute path to a relative path

This lesson demonstrates how to convert an absolute file path to a relative file path.

The relativePath function

The relativePath function takes two parameters

pFilePath: the absolute file path, that is, a file path that starts at the top of the folder structure and names each enclosing folder on the way to the file

pFolder: an optional folder where the relative path should start, if this parameter is not specified the current folder is used.

The function is called with a statement such as:

get relativePath("/Disk/Folder/File","/Disk/Folder")
put relativePath(it,myFolder) into pathToUse
set the fileLoc of me to relativePath(thisPath)

The relativePath function works by comparing the absolute path and the starting folder side by side, and removing the parts of the structure that are common to both paths. Then it figures out how many levels up the relative path needs to move to get to the starting folder, and prepends the appropriate number of "../" to the start of the relative path. Finally, it returns the new path.

Checking the pFilePath parameter

The first thing the function does is make sure pFilePath is, in fact, an absolute path. The rule for determining whether a path is an absolute path is as follows:

* On Windows systems, absolute paths begin with a drive letter followed by a colon character.

* On all other operating systems, absolute paths begin with a slash.

To figure out whether pFilePath meets these conditions, the function uses a complex expression with the and and or operators. If the function is running on Windows, and either the first character isn't a letter or the second character isn't a colon, this isn't an absolute path. If the function is running on another platform and the first character isn't a slash, this isn't an absolute path. These two conditions are each enclosed in parentheses and are joined by the or operator, so if either of them is true, the condition itself is true and LiveCode executes the contents of the if statement.

if (the platform is "Win32" and (char 1 of pFilePath is not in  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
      or char 2 of pFilePath is not ":")) \
      or (the platform is not "Win32" and \
      char 1 of pFilePath is not "/") then
   return "Error: path is not an absolute path"
end if

If pFilePath is not actually an absolute path, the handler uses the return control structure to pass an error message back to the calling handler.

Checking the pFolder parameter

Next, the handler checks pFolder. If nothing was passed in pFolder, the handler uses the current folder as the default for this parameter. You can use this method with any handler to assign a default value to one or more of the parameters. Just check whether the parameter is empty. If it is, then no value has been passed, and you can simply put the desired default into the parameter.

if pFolder is empty then
   put the defaultFolder into pFolder
end if

The relative path to the current folder is ".", so if pFilePath and pFolder are the same, the function returns ".".

if pFilePath is pFolder then 
   return "."
end if

Constructing the relative path

Now the handler begins comparing the two absolute paths, pFilePath and pFolder, to see what parts of the folder structure they have in common. Let's look at two paths to see how this comparison works.

Suppose pFolder is "/Disk/Applications/Spreadsheets/" and pFilePath is "/Disk/Applications/Graphics/ImageEdit". The first two parts, the first two items, with the itemDelimiter set to "/", are the same, so they won't be needed to create a relative path that starts from one and moves to the other.

set the itemDelimiter to "/"
repeat
   if item 1 of pFolder is item 1 of pFilePath then
      ## they're in the same disk or folder
      delete item 1 of pFilePath
      delete item 1 of pFolder
   else 
      exit repeat
   end if
end repeat

The repeat control structure moves through the two paths, item by item, and deletes the items they have in common. When the loop comes to an item that they don't have in common, it uses the exit repeat control structure to stop the loop. (Because we're using the simple repeat form of the repeat control structure--which can also be written repeat forever--we need to execute an exit repeat at some point. Otherwise, the repeat loop would just keep going.)

The pFolder variable now is "Spreadsheets/", and pFilePath is "Graphics/ImageEdit". This is the part of pFilePath that isn't in common with pFolder, so it's the part that's essential to finding the "ImageEdit" file from pFolder.

We need to do one more thing: figure out how many levels from pFolder to move up before we can find the "Graphics" folder. We can find this out by looking at what's left of pFolder. The number of items remaining in pFolder is the number of levels we will have to move up. The string "../" in a relative path means "go up one level", so we prepend this string the necessary number of times to pFilePath.

repeat for (the number of items in pFolder) times
   put "../" before pFilePath
end repeat
   
return pFilePath

In our example, there's one item left in pFolder, so the final relative path is "../Graphics/ImageEdit". This means "Starting at the Spreadsheets folder, go up one level to Applications. Then go down to the Graphics folder, and down once more to the ImageEdit file." You can read the relative path as a list of directions for how to start from the original pFolder and get to the destination.

The relativePath function code

function relativePath pFilePath, pFolder
   if (the platform is "Win32" and (char 1 of pFilePath is not in  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
         or char 2 of pFilePath is not ":")) \
         or (the platform is not "Win32" and \
         char 1 of pFilePath is not "/") then
      return "Error: path is not an absolute path"
   end if
   
   if pFolder is empty then
      put the defaultFolder into pFolder
   end if
   
   if pFilePath is pFolder then
      return "."
   end if
   
   set the itemDelimiter to "/"
   repeat
      if item 1 of pFolder is item 1 of pFilePath then
         ## they're in the same disk or folder
         delete item 1 of pFilePath
         delete item 1 of pFolder
      else 
         exit repeat
      end if
   end repeat
   
   repeat for (the number of items in pFolder) times
      put "../" before pFilePath
   end repeat
     
   return pFilePath
end relativePath

0 Comments

Add your comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.