os: add a glob() function (#10497)
							parent
							
								
									f029f7e897
								
							
						
					
					
						commit
						d2f19ac494
					
				|  | @ -0,0 +1,62 @@ | |||
| import os | ||||
| 
 | ||||
| fn deep_glob() ? { | ||||
| 	os.chdir(@VMODROOT) | ||||
| 	matches := os.glob('vlib/v/*/*.v') or { panic(err) } | ||||
| 	assert matches.len > 10 | ||||
| 	assert 'vlib/v/ast/ast.v' in matches | ||||
| 	assert 'vlib/v/ast/table.v' in matches | ||||
| 	assert 'vlib/v/token/token.v' in matches | ||||
| 	for f in matches { | ||||
| 		if !f.starts_with('vlib/v/') { | ||||
| 			assert false | ||||
| 		} | ||||
| 		assert f.ends_with('.v') | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn test_glob_can_find_v_files_3_levels_deep() ? { | ||||
| 	$if !windows { | ||||
| 		deep_glob() ? | ||||
| 	} | ||||
| 	assert true | ||||
| } | ||||
| 
 | ||||
| fn test_glob_can_find_files_in_current_folder() ? { | ||||
| 	os.chdir(@VMODROOT) | ||||
| 	matches := os.glob('*') ? | ||||
| 	assert 'README.md' in matches | ||||
| 	assert 'v.mod' in matches | ||||
| 	assert 'cmd/' in matches | ||||
| 	assert 'vlib/' in matches | ||||
| 	for f in matches { | ||||
| 		assert !f.ends_with('.v') | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn test_glob_can_be_used_with_multiple_patterns() ? { | ||||
| 	os.chdir(@VMODROOT) | ||||
| 	matches := os.glob('*', 'cmd/tools/*') ? | ||||
| 	assert 'README.md' in matches | ||||
| 	assert 'Makefile' in matches | ||||
| 	$if !windows { | ||||
| 		assert 'cmd/tools/test_if_v_test_system_works.v' in matches | ||||
| 	} | ||||
| 	$if windows { | ||||
| 		assert 'test_if_v_test_system_works.v' in matches | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn test_glob_star() ? { | ||||
| 	os.chdir(@VMODROOT) | ||||
| 	matches := os.glob('*ake*') ? | ||||
| 	assert 'Makefile' in matches | ||||
| 	assert 'make.bat' in matches | ||||
| } | ||||
| 
 | ||||
| fn test_glob_not_found() ? { | ||||
| 	os.glob('an_unknown_folder/*.v') or { | ||||
| 		assert true | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | @ -183,7 +183,7 @@ pub fn file_size(path string) u64 { | |||
| 		} | ||||
| 		$if x32 { | ||||
| 			$if debug { | ||||
| 				println('Using os.file_size() on 32bit systems may not work on big files.') | ||||
| 				eprintln('Using os.file_size() on 32bit systems may not work on big files.') | ||||
| 			} | ||||
| 			$if windows { | ||||
| 				if C._wstat(path.to_wide(), voidptr(&s)) != 0 { | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import strings | |||
| #include <sys/utsname.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/ptrace.h> | ||||
| #include <glob.h> | ||||
| 
 | ||||
| pub const ( | ||||
| 	path_separator = '/' | ||||
|  | @ -49,6 +50,14 @@ mut: | |||
| 	machine  &char | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.glob_t { | ||||
| mut: | ||||
| 	gl_pathc size_t // number of matched paths
 | ||||
| 	gl_pathv &&char // list of matched pathnames
 | ||||
| 	gl_offs  size_t // slots to reserve in gl_pathv
 | ||||
| } | ||||
| 
 | ||||
| fn C.uname(name voidptr) int | ||||
| 
 | ||||
| fn C.symlink(&char, &char) int | ||||
|  | @ -67,6 +76,37 @@ fn C.getgid() int | |||
| fn C.getegid() int | ||||
| 
 | ||||
| fn C.ptrace(u32, u32, voidptr, int) u64 | ||||
| fn C.glob(&char, int, voidptr, voidptr) int | ||||
| 
 | ||||
| fn C.globfree(voidptr) | ||||
| 
 | ||||
| pub fn glob(patterns ...string) ?[]string { | ||||
| 	mut matches := []string{} | ||||
| 	if patterns.len == 0 { | ||||
| 		return matches | ||||
| 	} | ||||
| 	mut globdata := C.glob_t{ | ||||
| 		gl_pathv: 0 | ||||
| 	} | ||||
| 	mut flags := int(C.GLOB_DOOFFS | C.GLOB_MARK) | ||||
| 	for i, pattern in patterns { | ||||
| 		if i > 0 { | ||||
| 			flags |= C.GLOB_APPEND | ||||
| 		} | ||||
| 		unsafe { | ||||
| 			if C.glob(&char(pattern.str), flags, C.NULL, &globdata) != 0 { | ||||
| 				return error_with_code(posix_get_error_msg(C.errno), C.errno) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	for i := 0; i < int(globdata.gl_pathc); i++ { | ||||
| 		unsafe { | ||||
| 			matches << cstring_to_vstring(globdata.gl_pathv[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	C.globfree(&globdata) | ||||
| 	return matches | ||||
| } | ||||
| 
 | ||||
| pub fn uname() Uname { | ||||
| 	mut u := Uname{} | ||||
|  |  | |||
|  | @ -699,3 +699,28 @@ fn test_truncate() { | |||
| fn test_hostname() { | ||||
| 	assert os.hostname().len > 2 | ||||
| } | ||||
| 
 | ||||
| fn test_glob() { | ||||
| 	os.mkdir('test_dir') or { panic(err) } | ||||
| 	for i in 0 .. 4 { | ||||
| 		if i == 3 { | ||||
| 			mut f := os.create('test_dir/test0_another') or { panic(err) } | ||||
| 			f.close() | ||||
| 			mut f1 := os.create('test_dir/test') or { panic(err) } | ||||
| 			f1.close() | ||||
| 		} else { | ||||
| 			mut f := os.create('test_dir/test' + i.str()) or { panic(err) } | ||||
| 			f.close() | ||||
| 		} | ||||
| 	} | ||||
| 	files := os.glob('test_dir/t*') or { panic(err) } | ||||
| 	assert files.len == 5 | ||||
| 	assert os.base(files[0]) == 'test' | ||||
| 
 | ||||
| 	for i in 0 .. 3 { | ||||
| 		os.rm('test_dir/test' + i.str()) or { panic(err) } | ||||
| 	} | ||||
| 	os.rm('test_dir/test0_another') or { panic(err) } | ||||
| 	os.rm('test_dir/test') or { panic(err) } | ||||
| 	os.rmdir_all('test_dir') or { panic(err) } | ||||
| } | ||||
|  |  | |||
|  | @ -94,6 +94,57 @@ fn init_os_args_wide(argc int, argv &&byte) []string { | |||
| 	return args_ | ||||
| } | ||||
| 
 | ||||
| pub fn glob(patterns ...string) ?[]string { | ||||
| 	mut matches := []string{} | ||||
| 	for pattern in patterns { | ||||
| 		windows_glob_pattern(pattern, mut matches) ? | ||||
| 	} | ||||
| 	return matches | ||||
| } | ||||
| 
 | ||||
| fn windows_glob_pattern(pattern string, mut matches []string) ? { | ||||
| 	$if debug { | ||||
| 		// FindFirstFile() and FindNextFile() both have a globbing function.
 | ||||
| 		// Unfortunately this is not as pronounced as under Unix, but should provide some functionality
 | ||||
| 		eprintln('os.glob() does not have all the features on Windows as it has on Unix operating systems') | ||||
| 	} | ||||
| 	mut find_file_data := Win32finddata{} | ||||
| 	wpattern := pattern.replace('/', '\\').to_wide() | ||||
| 	h_find_files := C.FindFirstFile(wpattern, voidptr(&find_file_data)) | ||||
| 
 | ||||
| 	defer { | ||||
| 		C.FindClose(h_find_files) | ||||
| 	} | ||||
| 
 | ||||
| 	if h_find_files == C.INVALID_HANDLE_VALUE { | ||||
| 		return error('os.glob(): Could not get a file handle: ' + | ||||
| 			get_error_msg(int(C.GetLastError()))) | ||||
| 	} | ||||
| 
 | ||||
| 	// save first finding
 | ||||
| 	fname := unsafe { string_from_wide(&find_file_data.c_file_name[0]) } | ||||
| 	if fname !in ['.', '..'] { | ||||
| 		mut fp := fname.replace('\\', '/') | ||||
| 		if find_file_data.dw_file_attributes & u32(C.FILE_ATTRIBUTE_DIRECTORY) > 0 { | ||||
| 			fp += '/' | ||||
| 		} | ||||
| 		matches << fp | ||||
| 	} | ||||
| 
 | ||||
| 	// check and save next findings
 | ||||
| 	for i := 0; C.FindNextFile(h_find_files, voidptr(&find_file_data)) > 0; i++ { | ||||
| 		filename := unsafe { string_from_wide(&find_file_data.c_file_name[0]) } | ||||
| 		if filename in ['.', '..'] { | ||||
| 			continue | ||||
| 		} | ||||
| 		mut fpath := filename.replace('\\', '/') | ||||
| 		if find_file_data.dw_file_attributes & u32(C.FILE_ATTRIBUTE_DIRECTORY) > 0 { | ||||
| 			fpath += '/' | ||||
| 		} | ||||
| 		matches << fpath | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn ls(path string) ?[]string { | ||||
| 	mut find_file_data := Win32finddata{} | ||||
| 	mut dir_files := []string{} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue