| 
									
										
										
										
											2017-04-03 08:57:33 +02:00
										 |  |  | package sftp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-04-03 21:42:41 +02:00
										 |  |  | 	"restic/errors" | 
					
						
							| 
									
										
										
										
											2017-04-03 08:57:33 +02:00
										 |  |  | 	"unicode" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // shellSplitter splits a command string into separater arguments. It supports | 
					
						
							|  |  |  | // single and double quoted strings. | 
					
						
							|  |  |  | type shellSplitter struct { | 
					
						
							|  |  |  | 	quote    rune | 
					
						
							|  |  |  | 	lastChar rune | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *shellSplitter) isSplitChar(c rune) bool { | 
					
						
							|  |  |  | 	// only test for quotes if the last char was not a backslash | 
					
						
							|  |  |  | 	if s.lastChar != '\\' { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// quote ended | 
					
						
							|  |  |  | 		if s.quote != 0 && c == s.quote { | 
					
						
							|  |  |  | 			s.quote = 0 | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// quote starts | 
					
						
							|  |  |  | 		if s.quote == 0 && (c == '"' || c == '\'') { | 
					
						
							|  |  |  | 			s.quote = c | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s.lastChar = c | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// within quote | 
					
						
							|  |  |  | 	if s.quote != 0 { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// outside quote | 
					
						
							|  |  |  | 	return c == '\\' || unicode.IsSpace(c) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SplitShellArgs returns the list of arguments from a shell command string. | 
					
						
							| 
									
										
										
										
											2017-04-03 21:05:42 +02:00
										 |  |  | func SplitShellArgs(data string) (cmd string, args []string, err error) { | 
					
						
							| 
									
										
										
										
											2017-04-03 08:57:33 +02:00
										 |  |  | 	s := &shellSplitter{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// derived from strings.SplitFunc | 
					
						
							|  |  |  | 	fieldStart := -1 // Set to -1 when looking for start of field. | 
					
						
							|  |  |  | 	for i, rune := range data { | 
					
						
							|  |  |  | 		if s.isSplitChar(rune) { | 
					
						
							|  |  |  | 			if fieldStart >= 0 { | 
					
						
							| 
									
										
										
										
											2017-04-03 21:05:42 +02:00
										 |  |  | 				args = append(args, data[fieldStart:i]) | 
					
						
							| 
									
										
										
										
											2017-04-03 08:57:33 +02:00
										 |  |  | 				fieldStart = -1 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else if fieldStart == -1 { | 
					
						
							|  |  |  | 			fieldStart = i | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if fieldStart >= 0 { // Last field might end at EOF. | 
					
						
							| 
									
										
										
										
											2017-04-03 21:05:42 +02:00
										 |  |  | 		args = append(args, data[fieldStart:]) | 
					
						
							| 
									
										
										
										
											2017-04-03 08:57:33 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch s.quote { | 
					
						
							|  |  |  | 	case '\'': | 
					
						
							| 
									
										
										
										
											2017-04-03 21:05:42 +02:00
										 |  |  | 		return "", nil, errors.New("single-quoted string not terminated") | 
					
						
							| 
									
										
										
										
											2017-04-03 08:57:33 +02:00
										 |  |  | 	case '"': | 
					
						
							| 
									
										
										
										
											2017-04-03 21:05:42 +02:00
										 |  |  | 		return "", nil, errors.New("double-quoted string not terminated") | 
					
						
							| 
									
										
										
										
											2017-04-03 08:57:33 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 21:05:42 +02:00
										 |  |  | 	if len(args) == 0 { | 
					
						
							|  |  |  | 		return "", nil, errors.New("command string is empty") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd, args = args[0], args[1:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return cmd, args, nil | 
					
						
							| 
									
										
										
										
											2017-04-03 08:57:33 +02:00
										 |  |  | } |