Beispiel #1
0
/* Canonicalize path, and return a new path.  Do everything in situ.  The new
   path differs from path in:

     Multiple `/'s are collapsed to a single `/'.
     Leading `./'s and trailing `/.'s are removed.
     Non-leading `../'s and trailing `..'s are handled by removing
     portions of the path.  */
static gchar *
mate_vfs_canonicalize_pathname (gchar *path)
{
	int i, marker;

	if (path == NULL || strlen (path) == 0) {
		return "";
	}

	/* Walk along path looking for things to compact. */
	for (i = 0, marker = 0;;) {
		if (!path[i])
			break;

		/* Check for `../', `./' or trailing `.' by itself. */
		if (path[i] == '.') {
			/* Handle trailing `.' by itself. */
			if (path[i + 1] == '\0') {
				if (i > 1 && path[i - 1] == MATE_VFS_URI_PATH_CHR) {
					/* strip the trailing /. */
					path[i - 1] = '\0';
				} else {
					/* convert path "/." to "/" */
					path[i] = '\0';
				}
				break;
			}

			/* Handle `./'. */
			if (path[i + 1] == MATE_VFS_URI_PATH_CHR) {
				memmove (path + i, path + i + 2,
					 strlen (path + i + 2) + 1);
				if (i == 0) {
					/* don't leave leading '/' for paths that started
					 * as relative (.//foo)
					 */
					collapse_slash_runs (path, i);
					marker = 0;
				}
				continue;
			}

			/* Handle `../' or trailing `..' by itself.
			 * Remove the previous xxx/ part
			 */
			if (path[i + 1] == '.'
			    && (path[i + 2] == MATE_VFS_URI_PATH_CHR
				|| path[i + 2] == '\0')) {

				/* ignore ../ at the beginning of a path */
				if (i != 0) {
					marker = find_slash_before_offset (path, i - 1);

					/* Either advance past '/' or point to the first character */
					marker ++;
					if (path [i + 2] == '\0' && marker > 1) {
						/* If we are looking at a /.. at the end of the uri and we
						 * need to eat the last '/' too.
						 */
						 marker--;
					}
					g_assert(marker < i);

					if (path[i + 2] == MATE_VFS_URI_PATH_CHR) {
						/* strip the entire ../ string */
						i++;
					}

					memmove (path + marker, path + i + 2,
						 strlen (path + i + 2) + 1);
					i = marker;
				} else {
					i = 2;
					if (path[i] == MATE_VFS_URI_PATH_CHR) {
						i++;
					}
				}
				collapse_slash_runs (path, i);
				continue;
			}
		}

		/* advance to the next '/' */
		i = find_next_slash (path, i);

		/* If we didn't find any slashes, then there is nothing left to do. */
		if (i < 0) {
			break;
		}

		marker = i++;
		collapse_slash_runs (path, i);
	}
	return path;
}
/**
 * thunar_vfs_canonicalize_filename:
 * @filename : a local filename.
 *
 * Canonicalizes @filename and returns a new path. The new path
 * differs from @filename in:
 *
 * <simplelist>
 * <member>Multiple `/'s are collapsed to a single `/'.</member>
 * <member>Leading `./'s and trailing `/.'s are removed.</member>
 * <member>Non-leading `../'s and trailing `..'s are handled by removing portions of the path.</member>
 * </simplelist>
 *
 * The caller is responsible to free the returned string using
 * g_free() when no longer needed.
 *
 * Return value: the canonicalized path for @filename.
 **/
gchar*
thunar_vfs_canonicalize_filename (const gchar *filename)
{
  gchar *path;
  gint   marker;
  gint   i;

  g_return_val_if_fail (filename != NULL, NULL);

  /* take a copy of the filename to operate on */
  path = g_strdup (filename);
	if (G_UNLIKELY (*path == '\0'))
    return path;

	/* Walk along path looking for things to compact. */
	for (i = 0, marker = 0;;)
    {
  		if (G_UNLIKELY (path[i] == '\0'))
	  		break;

      /* Check for `../', `./' or trailing `.' by itself. */
      if (path[i] == '.')
        {
          /* Handle trailing `.' by itself. */
          if (path[i + 1] == '\0')
            {
              if (i > 1 && path[i - 1] == G_DIR_SEPARATOR)
                {
                  /* strip the trailing /. */
                  path[i - 1] = '\0';
                }
              else
                {
                  /* convert path "/." to "/" */
                  path[i] = '\0';
                }
              break;
            }

          /* Handle `./'. */
          if (path[i + 1] == G_DIR_SEPARATOR)
            {
              memmove (path + i, path + i + 2, strlen (path + i + 2) + 1);
              if (i == 0)
                {
                  /* don't leave leading '/' for paths that started
                   * as relative (.//foo)
                   */
                  collapse_slash_runs (path, i);
                  marker = 0;
                }
              continue;
            }

          /* Handle `../' or trailing `..' by itself. 
           * Remove the previous xxx/ part 
           */
          if (path[i + 1] == '.' && (path[i + 2] == G_DIR_SEPARATOR || path[i + 2] == '\0'))
            {
              /* ignore ../ at the beginning of a path */
              if (i != 0)
                {
                  marker = find_slash_before_offset (path, i - 1);

                  /* Either advance past '/' or point to the first character */
                  marker ++;
                  if (path [i + 2] == '\0' && marker > 1)
                    {
                      /* If we are looking at a /.. at the end of the uri and we
                       * need to eat the last '/' too.
                       */
                       marker--;
                    }
                  
                  /* strip the entire ../ string */
                  if (path[i + 2] == G_DIR_SEPARATOR)
                    ++i;

                  memmove (path + marker, path + i + 2, strlen (path + i + 2) + 1);
                  i = marker;
                }
              else
                {
                  i = 2;
                  if (path[i] == G_DIR_SEPARATOR)
                    i++;
                }
              
              collapse_slash_runs (path, i);
              continue;
            }
        }
      
      /* advance to the next '/' */
      i = find_next_slash (path, i);

      /* If we didn't find any slashes, then there is nothing left to do. */
      if (i < 0)
        break;

      marker = i++;
      collapse_slash_runs (path, i);
    }

	return path;
}