Having trouble with backslash outside quotes

I’m stuck on stage: #YT5

The problem I have is the 2nd case isn’t even triggering, can you tell why and any tips on how to do it.

My code:

package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"slices"
	"strings"
)

var builtins = []string{"exit", "echo", "type", "pwd"}

func FindPath(val string, paths []string) (string, bool) {
	for _, path := range paths {
		file := path + "/" + val
		if _, err := os.Stat(file); err == nil {
			return file, true
		}
	}
	return "", false
}

func RemoveQuotes(input string) (string, []string) {
	var word string
	var output string
	var newArr []string
	var quoteChar rune
	if strings.Contains(input, "'") || strings.Contains(input, `"`) || strings.Contains(input, `\`) || strings.Contains(input, `/`) {

		for i := 0; i < len(input); i++ {
			ch := rune(input[i])

			switch ch {
			case '\'', '"':
				if quoteChar == 0 {
					quoteChar = ch
				} else if quoteChar == ch {
					if !(i+1 < len(input) && (input[i+1] == '\'' || input[i+1] == '"')) {
						newArr = append(newArr, word)
						word = ""
					}
					quoteChar = 0
				} else {
					word += string(ch)
				}
			case '\\', '/':
				if i+1 < len(input) {
					next := rune(input[i+1])
					word += string(next)
					i++
				}
			default:
				if quoteChar != 0 {
					word += string(ch)
				}
			}
		}

		if word != "" && quoteChar != 0 {
			newArr = append(newArr, word)
		}

		singlesRemoved := strings.ReplaceAll(input, "'", "")
		doublesRemoved := strings.ReplaceAll(singlesRemoved, `"`, "")
		output = doublesRemoved
	} else {
		output = input
	}
	return output, newArr
}

func main() {

	for {
		fmt.Fprint(os.Stdout, "$ ")

		paths := strings.Split(os.Getenv("PATH"), ":")
		input, err := bufio.NewReader(os.Stdin).ReadString('\n')
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error reading input: ", err)
			os.Exit(1)
		}

		cmdArr := strings.Fields(input)
		cmd := strings.TrimSpace(cmdArr[0])

		var args []string

		newInput, newArgsArr := RemoveQuotes(input)

		if strings.Contains(input, "'") || strings.Contains(input, `"`) {
			input = newInput
			args = newArgsArr
		} else if len(newArgsArr) == 0 {
			args = cmdArr[1:]
		} else {
			args = cmdArr[1:]
		}

		if strings.TrimSpace(input) == "exit 0" {
			break
		}

		switch cmd {
		case "echo":
			EchoCmd(args)
		case "type":
			TypeCmd(cmdArr, paths)
		case "pwd":
			Pwd()
		case "cd":
			Cd(args)
		default:
			filepath, exists := FindPath(cmd, paths)
			if exists && filepath != "" {
				CustomExeCmd(cmd, args)
			} else {
				fmt.Println(cmd + ": command not found")
			}
		}
	}

}

func EchoCmd(args []string) {
	output := strings.Join(args, " ")
	fmt.Println(output)
}

func TypeCmd(cmdArr []string, paths []string) {
	if len(cmdArr) == 1 {
		return
	}

	value := cmdArr[1]

	if slices.Contains(builtins, value) {
		fmt.Println(value + " is a shell builtin")
		return
	}

	if filePath, exists := FindPath(value, paths); exists {
		fmt.Println(value + " is " + filePath)
		return
	}

	fmt.Println(value + ": not found")
}

func CustomExeCmd(cmd string, args []string) {
	exc := exec.Command(cmd, args...)
	exc.Stdout = os.Stdout
	exc.Stderr = os.Stderr
	exc.Run()
}

func Pwd() {
	dir, err := os.Getwd()
	if err != nil {
		return
	}
	fmt.Println(dir)
}

func Cd(args []string) {
	path := strings.Join(args, "")
	homepath := os.Getenv("HOME")
	formatedPath := strings.ReplaceAll(path, "~", homepath)
	err := os.Chdir(formatedPath)
	if err != nil {
		fmt.Println("cd: " + path + ": No such file or directory")
	}
}

the specific function:

func RemoveQuotes(input string) (string, []string) {
	var word string
	var output string
	var newArr []string
	var quoteChar rune
	if strings.Contains(input, "'") || strings.Contains(input, `"`) || strings.Contains(input, `\`) || strings.Contains(input, `/`) {

		for i := 0; i < len(input); i++ {
			ch := rune(input[i])

			switch ch {
			case '\'', '"':
				if quoteChar == 0 {
					quoteChar = ch
				} else if quoteChar == ch {
					if !(i+1 < len(input) && (input[i+1] == '\'' || input[i+1] == '"')) {
						newArr = append(newArr, word)
						word = ""
					}
					quoteChar = 0
				} else {
					word += string(ch)
				}
			case '\\', '/':
				if i+1 < len(input) {
					next := rune(input[i+1])
					word += string(next)
					i++
				}
			default:
				if quoteChar != 0 {
					word += string(ch)
				}
			}
		}

		if word != "" && quoteChar != 0 {
			newArr = append(newArr, word)
		}

		singlesRemoved := strings.ReplaceAll(input, "'", "")
		doublesRemoved := strings.ReplaceAll(singlesRemoved, `"`, "")
		output = doublesRemoved
	} else {
		output = input
	}
	return output, newArr
}

Output:

lucifer@LUCIFER-1O1:~/codecrafters-shell-go$ go run app/main.go 
$ echo world\ \ \ \ \ \ script
world\ \ \ \ \ \ script
$ 

Hey @logan1o1, could you upload your code to GitHub and share the link? It will be much easier to debug if I can run it directly.

Nvm problem solved

@logan1o1 Congrats! :tada:

Mind sharing what was wrong? Would love to see if we can improve the tester / instructions.

There was a test case in the suit that was not mentioned in the instructions, and overall, it was just a little complicated. I don’t remember exactly what it was though.

But I not sure if it’s too big of a problem, cause I’d say most of the problems I faced is cause I am a beginner, but I’d say some useful hints along with the instructions would be more convenient.

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.