Menu Close

Go – Simulation of Linux cd command in Go

Here, we will see Simulation of Linux cd command in Go. We will do directory changes without OS call with program.

SimulatingĀ a “cd” Linux command that changes a current directory in a file system. The simulated command takes two path strings from the command line and prints either a new path or an error.

The first path is a current working directory and second path is a new directory. Here a directory name can only contain alphanumeric characters.

  • A single dot (“.”) indicates a current directory.
  • the two dots (“..”) indicate a step to a previous directory, up from the current one.
  • A single forward slash “/” indicates a root directory.
  • Multiple consecutive slashes (////) are treated as equivalent to one (/).

You have to print a new path.

Example:

$ go build mycd.go

$ ./mycd / abc
Output: /abc

$ mycd /abc/def ghi
Output: /abc/def/ghi

$ mycd /abc/def ..
Output: /abc

$ mycd /abc/def /abc
Output: /abc

$ mycd /abc/def /abc/klm
Output: /abc/klm

$ mycd /abc/def ../..
Output: /

$ mycd /abc/def ../../..
Output: /

$ mycd /abc/def .
Output: /abc/def

$ mycd /abc/def ..klm
Output: ..klm: No such file or directory

$ mycd /abc/def //////
Output: /

$ mycd /abc/def ......
Output: ......: No such file or directory

$ mycd /abc/def ../gh///../klm/.
Output: /abc/klm

Code to Simulation of Linux cd command in Go

package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"
)

func remove_index_from_slice_of_strings(slice []string, index int) []string {
	return append(slice[:index], slice[index+1:]...)
}

func panic_recovery(err error) {
	if err := recover(); err != nil { //catch
		fmt.Println("mycd: ", err)
		os.Exit(1)
	}
}

func is_alphanum(word string) bool {
	return regexp.MustCompile(`^[a-zA-Z0-9]*$`).MatchString(word)
}

func remove_empty_index_in_slice(slice []string, from_index int) []string {
	for i := from_index; i < len(slice); {
		if strings.Compare(slice[i], "") == 0 {
			slice = remove_index_from_slice_of_strings(slice, i)
		} else {
			break
		}
	}
	return slice
}

func validate_and_convert_dir_path_to_slice(dir_path string) []string {

	dir_path_list := strings.Split(dir_path, "/")

	if strings.Compare(dir_path_list[0], "") == 0 {
		dir_path_list = remove_empty_index_in_slice(dir_path_list, 1)
	}

	for i, dir_name := range dir_path_list {
		// Validate if dir name is alphanumeric or not
		if !(is_alphanum(dir_name) ||
			(strings.Compare(dir_name, "..") == 0) ||
			(strings.Compare(dir_name, ".") == 0)) {
			panic(dir_name + ": No such file or directory")
		}
		// Remove extra backslashes if persists (In strings.Split with "/"
		// converts backslashes to empty string)
		if strings.Compare(dir_name, "") == 0 {
			if i == 0 {
				// ignore first backslash if exists
				continue
			}
			dir_path_list = remove_empty_index_in_slice(dir_path_list, i)
		}
	}

	return dir_path_list
}

func back_to_the_child_path(cwd []string) []string {
	return append(cwd[:len(cwd)-1], cwd[len(cwd):]...)
}

func prepare_new_dir(cwd_dir_path_list, new_dir_path_list []string) string {

	new_dir := "/"

	if strings.Compare(new_dir_path_list[0], "") == 0 {
		// If target path starts with "/". Checking with empty string because In strings.Split with "/"
		// "/" converts backslash to empty string in slice of strings
		if len(new_dir_path_list) == 1 && strings.Compare(new_dir_path_list[0], "") == 0 {
			// If target has only backslash "/"
			return new_dir
		}
		new_dir = strings.Join(new_dir_path_list, "/")
		return new_dir
	}

	for i := 0; i < len(new_dir_path_list); i++ {
		if strings.Compare(new_dir_path_list[i], "") == 0 ||
			strings.Compare(new_dir_path_list[i], ".") == 0 {
			continue
		}
		if strings.Compare(new_dir_path_list[i], "..") == 0 {
			if len(cwd_dir_path_list) > 1 {
				cwd_dir_path_list = back_to_the_child_path(cwd_dir_path_list)
			}
		} else {
			cwd_dir_path_list = append(cwd_dir_path_list, new_dir_path_list[i])
		}
	}
	if len(cwd_dir_path_list) == 1 && strings.Compare(cwd_dir_path_list[0], "") == 0 {
		return new_dir
	} else {
		new_dir = strings.Join(cwd_dir_path_list, "/")
		return new_dir
	}
}

func main() {
	defer panic_recovery(nil)

	if len(os.Args) < 3 {
		panic("too few arguments")
	} else if len(os.Args) > 3 {
		panic("too many arguments")
	}

	cwd := os.Args[1]
	to_dirctory := os.Args[2]

	cwd_dir_path_list := validate_and_convert_dir_path_to_slice(cwd)
	new_dir_path_list := validate_and_convert_dir_path_to_slice(to_dirctory)

	new_dir := prepare_new_dir(cwd_dir_path_list, new_dir_path_list)

	fmt.Println(new_dir)
}

Output:

% go build mycd.go
% ./mycd /abc wd/ss/../dsds/../dd/ddee/./../
Output: /abc/wd/dd

To check more leetcode problem’s solution. Pls click given below link:

https://www.techieindoor.com/category/leetcode/

Posted in golang, Miscellaneous

Leave a Reply

Your email address will not be published. Required fields are marked *