27145de12e3d1e6401bea32ed814e033af812f6a — dragnel 25 days ago 57e0022 master
Add command -v builtin.
6 files changed, 107 insertions(+), 0 deletions(-)

M builtin/builtin.c
A builtin/command.c
M include/builtin.h
M meson.build
A test/command.sh
M test/meson.build
M builtin/builtin.c => builtin/builtin.c +1 -0
@@ 19,6 19,7 @@ { "alias", builtin_alias, false },
  	{ "bg", builtin_bg, false },
  	{ "cd", builtin_cd, false },
+ 	{ "command", builtin_command, false },
  	{ "eval", builtin_eval, true },
  	{ "exit", builtin_exit, true },
  	{ "export", builtin_export, true },

A builtin/command.c => builtin/command.c +78 -0
@@ 0,0 1,78 @@
+ #define _POSIX_C_SOURCE 200809L
+ #include "parser.h"
+ #include "mrsh/getopt.h"
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/stat.h>
+ #include "shell/path.h"
+ #include "mrsh/builtin.h"
+ 
+ static const char command_usage[] = "usage: command [-v|-V|-p] "
+ 	"command_name [argument...]\n";
+ 
+ static int command_v(struct mrsh_state *state, const char *command_name) {
+ 	size_t len_command_name = strlen(command_name);
+ 
+ 	const char *look_alias =
+ 		mrsh_hashtable_get(&state->aliases, command_name);
+ 	if (look_alias != NULL) {
+ 		printf("alias %s='%s'\n", command_name, look_alias);
+ 		return 0;
+ 	}
+ 
+ 	const char *look_fn =
+ 		mrsh_hashtable_get(&state->functions, command_name);
+ 	if (look_fn != NULL) {
+ 		printf("%s\n", command_name);
+ 		return 0;
+ 	}
+ 
+ 	if (mrsh_has_builtin(command_name)) {
+ 		printf("%s\n", command_name);
+ 		return 0;
+ 	}
+ 
+ 	for (size_t i = 0; i < keywords_len; ++i) {
+ 		if (strlen(keywords[i]) == len_command_name &&
+ 				strcmp(command_name, keywords[i]) == 0) {
+ 			printf("%s\n", command_name);
+ 			return 0;
+ 		}
+ 	}
+ 
+ 	const char *expanded = expand_path(state, command_name, 1);
+ 	if (expanded != NULL) {
+ 		printf("%s\n", expanded);
+ 		return 0;
+ 	}
+ 
+ 	return 127;
+ }
+ 
+ int builtin_command(struct mrsh_state *state, int argc, char *argv[]) {
+ 	mrsh_optind = 0;
+ 	int opt;
+ 
+ 	while ((opt = mrsh_getopt(argc, argv, ":vVp")) != -1) {
+ 		switch (opt) {
+ 		case 'v':
+ 			if (argc != 3) {
+ 				return 1;
+ 			}
+ 			return command_v(state, argv[mrsh_optind]);
+ 		case 'V':
+ 		case 'p':
+ 			fprintf(stderr, "command: `-V` and `-p` and no arg not "
+ 					"yet implemented\n");
+ 			return 1;
+ 		default:
+ 			fprintf(stderr, "command: unknown option -- %c\n",
+ 					mrsh_optopt);
+ 			fprintf(stderr, command_usage);
+ 			return 1;
+ 		}
+ 	}
+ 
+ 	return 0;
+ }

M include/builtin.h => include/builtin.h +1 -0
@@ 11,6 11,7 @@ int builtin_alias(struct mrsh_state *state, int argc, char *argv[]);
  int builtin_bg(struct mrsh_state *state, int argc, char *argv[]);
  int builtin_cd(struct mrsh_state *state, int argc, char *argv[]);
+ int builtin_command(struct mrsh_state *state, int argc, char *argv[]);
  int builtin_colon(struct mrsh_state *state, int argc, char *argv[]);
  int builtin_dot(struct mrsh_state *state, int argc, char *argv[]);
  int builtin_eval(struct mrsh_state *state, int argc, char *argv[]);

M meson.build => meson.build +1 -0
@@ 77,6 77,7 @@ 'builtin/builtin.c',
  		'builtin/cd.c',
  		'builtin/colon.c',
+ 		'builtin/command.c',
  		'builtin/dot.c',
  		'builtin/eval.c',
  		'builtin/exit.c',

A test/command.sh => test/command.sh +25 -0
@@ 0,0 1,25 @@
+ #!/bin/sh
+ 
+ lla () {
+ 	ls -la
+ }
+ 
+ alias ll="ls -l"
+ 
+ command -v if
+ echo "exists if $?"
+ command -v cd
+ echo "exits cd $?"
+ command -v ls
+ echo "exists ls $?"
+ command -v ll
+ echo "exists ll $?"
+ command -v lla
+ echo "exists lla $?"
+ command -v idontexists
+ if [ $? -ne 0 ]
+ then
+ 	echo "ok"
+ else
+ 	echo "ko"
+ fi

M test/meson.build => test/meson.build +1 -0
@@ 11,6 11,7 @@ 'subshell.sh',
  	'ulimit.sh',
  	'word.sh',
+ 	'command.sh',
  ]
  
  foreach test_file : test_files