mirror of
https://github.com/ukui/kernel.git
synced 2026-03-09 10:07:04 -07:00
perf_counter tools: add in basic glue from Git
First very raw version at having a central 'perf' command and a list of subcommands: perf top perf stat perf record perf report ... This is done by picking up Git's collection of utility functions, and hacking them to build fine in this new environment. Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
GIT-BUILD-OPTIONS
|
||||
GIT-CFLAGS
|
||||
GIT-GUI-VARS
|
||||
GIT-VERSION-FILE
|
||||
git
|
||||
git-add
|
||||
git-add--interactive
|
||||
git-am
|
||||
git-annotate
|
||||
git-apply
|
||||
git-archimport
|
||||
git-archive
|
||||
git-bisect
|
||||
git-bisect--helper
|
||||
git-blame
|
||||
git-branch
|
||||
git-bundle
|
||||
git-cat-file
|
||||
git-check-attr
|
||||
git-check-ref-format
|
||||
git-checkout
|
||||
git-checkout-index
|
||||
git-cherry
|
||||
git-cherry-pick
|
||||
git-clean
|
||||
git-clone
|
||||
git-commit
|
||||
git-commit-tree
|
||||
git-config
|
||||
git-count-objects
|
||||
git-cvsexportcommit
|
||||
git-cvsimport
|
||||
git-cvsserver
|
||||
git-daemon
|
||||
git-diff
|
||||
git-diff-files
|
||||
git-diff-index
|
||||
git-diff-tree
|
||||
git-difftool
|
||||
git-difftool--helper
|
||||
git-describe
|
||||
git-fast-export
|
||||
git-fast-import
|
||||
git-fetch
|
||||
git-fetch--tool
|
||||
git-fetch-pack
|
||||
git-filter-branch
|
||||
git-fmt-merge-msg
|
||||
git-for-each-ref
|
||||
git-format-patch
|
||||
git-fsck
|
||||
git-fsck-objects
|
||||
git-gc
|
||||
git-get-tar-commit-id
|
||||
git-grep
|
||||
git-hash-object
|
||||
git-help
|
||||
git-http-fetch
|
||||
git-http-push
|
||||
git-imap-send
|
||||
git-index-pack
|
||||
git-init
|
||||
git-init-db
|
||||
git-instaweb
|
||||
git-log
|
||||
git-lost-found
|
||||
git-ls-files
|
||||
git-ls-remote
|
||||
git-ls-tree
|
||||
git-mailinfo
|
||||
git-mailsplit
|
||||
git-merge
|
||||
git-merge-base
|
||||
git-merge-index
|
||||
git-merge-file
|
||||
git-merge-tree
|
||||
git-merge-octopus
|
||||
git-merge-one-file
|
||||
git-merge-ours
|
||||
git-merge-recursive
|
||||
git-merge-resolve
|
||||
git-merge-subtree
|
||||
git-mergetool
|
||||
git-mergetool--lib
|
||||
git-mktag
|
||||
git-mktree
|
||||
git-name-rev
|
||||
git-mv
|
||||
git-pack-redundant
|
||||
git-pack-objects
|
||||
git-pack-refs
|
||||
git-parse-remote
|
||||
git-patch-id
|
||||
git-peek-remote
|
||||
git-prune
|
||||
git-prune-packed
|
||||
git-pull
|
||||
git-push
|
||||
git-quiltimport
|
||||
git-read-tree
|
||||
git-rebase
|
||||
git-rebase--interactive
|
||||
git-receive-pack
|
||||
git-reflog
|
||||
git-relink
|
||||
git-remote
|
||||
git-repack
|
||||
git-repo-config
|
||||
git-request-pull
|
||||
git-rerere
|
||||
git-reset
|
||||
git-rev-list
|
||||
git-rev-parse
|
||||
git-revert
|
||||
git-rm
|
||||
git-send-email
|
||||
git-send-pack
|
||||
git-sh-setup
|
||||
git-shell
|
||||
git-shortlog
|
||||
git-show
|
||||
git-show-branch
|
||||
git-show-index
|
||||
git-show-ref
|
||||
git-stage
|
||||
git-stash
|
||||
git-status
|
||||
git-stripspace
|
||||
git-submodule
|
||||
git-svn
|
||||
git-symbolic-ref
|
||||
git-tag
|
||||
git-tar-tree
|
||||
git-unpack-file
|
||||
git-unpack-objects
|
||||
git-update-index
|
||||
git-update-ref
|
||||
git-update-server-info
|
||||
git-upload-archive
|
||||
git-upload-pack
|
||||
git-var
|
||||
git-verify-pack
|
||||
git-verify-tag
|
||||
git-web--browse
|
||||
git-whatchanged
|
||||
git-write-tree
|
||||
git-core-*/?*
|
||||
gitk-wish
|
||||
gitweb/gitweb.cgi
|
||||
test-chmtime
|
||||
test-ctype
|
||||
test-date
|
||||
test-delta
|
||||
test-dump-cache-tree
|
||||
test-genrandom
|
||||
test-match-trees
|
||||
test-parse-options
|
||||
test-path-utils
|
||||
test-sha1
|
||||
test-sigchain
|
||||
common-cmds.h
|
||||
*.tar.gz
|
||||
*.dsc
|
||||
*.deb
|
||||
git.spec
|
||||
*.exe
|
||||
*.[aos]
|
||||
*.py[co]
|
||||
config.mak
|
||||
autom4te.cache
|
||||
config.cache
|
||||
config.log
|
||||
config.status
|
||||
config.mak.autogen
|
||||
config.mak.append
|
||||
configure
|
||||
tags
|
||||
TAGS
|
||||
cscope*
|
||||
+172
-853
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
SHELL_PATH='/bin/sh'
|
||||
TAR='tar'
|
||||
NO_CURL=''
|
||||
NO_PERL=''
|
||||
@@ -0,0 +1 @@
|
||||
-g -O2 -Wall -DSHA1_HEADER='<openssl/sha.h>' : /home/mingo/bin:libexec/perf-core:share/perf-core/templates:/home/mingo
|
||||
@@ -0,0 +1 @@
|
||||
PERF_VERSION = 0.0.1.PERF
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
GVF=PERF-VERSION-FILE
|
||||
DEF_VER=v0.0.1.PERF
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
# First see if there is a version file (included in release tarballs),
|
||||
# then try git-describe, then default.
|
||||
if test -f version
|
||||
then
|
||||
VN=$(cat version) || VN="$DEF_VER"
|
||||
elif test -d .git -o -f .git &&
|
||||
VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
|
||||
case "$VN" in
|
||||
*$LF*) (exit 1) ;;
|
||||
v[0-9]*)
|
||||
git update-index -q --refresh
|
||||
test -z "$(git diff-index --name-only HEAD --)" ||
|
||||
VN="$VN-dirty" ;;
|
||||
esac
|
||||
then
|
||||
VN=$(echo "$VN" | sed -e 's/-/./g');
|
||||
else
|
||||
VN="$DEF_VER"
|
||||
fi
|
||||
|
||||
VN=$(expr "$VN" : v*'\(.*\)')
|
||||
|
||||
if test -r $GVF
|
||||
then
|
||||
VC=$(sed -e 's/^PERF_VERSION = //' <$GVF)
|
||||
else
|
||||
VC=unset
|
||||
fi
|
||||
test "$VN" = "$VC" || {
|
||||
echo >&2 "PERF_VERSION = $VN"
|
||||
echo "PERF_VERSION = $VN" >$GVF
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
#include "cache.h"
|
||||
|
||||
/*
|
||||
* Do not use this for inspecting *tracked* content. When path is a
|
||||
* symlink to a directory, we do not want to say it is a directory when
|
||||
* dealing with tracked content in the working tree.
|
||||
*/
|
||||
int is_directory(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
return (!stat(path, &st) && S_ISDIR(st.st_mode));
|
||||
}
|
||||
|
||||
/* We allow "recursive" symbolic links. Only within reason, though. */
|
||||
#define MAXDEPTH 5
|
||||
|
||||
const char *make_absolute_path(const char *path)
|
||||
{
|
||||
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
|
||||
char cwd[1024] = "";
|
||||
int buf_index = 1, len;
|
||||
|
||||
int depth = MAXDEPTH;
|
||||
char *last_elem = NULL;
|
||||
struct stat st;
|
||||
|
||||
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
|
||||
die ("Too long path: %.*s", 60, path);
|
||||
|
||||
while (depth--) {
|
||||
if (!is_directory(buf)) {
|
||||
char *last_slash = strrchr(buf, '/');
|
||||
if (last_slash) {
|
||||
*last_slash = '\0';
|
||||
last_elem = xstrdup(last_slash + 1);
|
||||
} else {
|
||||
last_elem = xstrdup(buf);
|
||||
*buf = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (*buf) {
|
||||
if (!*cwd && !getcwd(cwd, sizeof(cwd)))
|
||||
die ("Could not get current working directory");
|
||||
|
||||
if (chdir(buf))
|
||||
die ("Could not switch to '%s'", buf);
|
||||
}
|
||||
if (!getcwd(buf, PATH_MAX))
|
||||
die ("Could not get current working directory");
|
||||
|
||||
if (last_elem) {
|
||||
int len = strlen(buf);
|
||||
if (len + strlen(last_elem) + 2 > PATH_MAX)
|
||||
die ("Too long path name: '%s/%s'",
|
||||
buf, last_elem);
|
||||
buf[len] = '/';
|
||||
strcpy(buf + len + 1, last_elem);
|
||||
free(last_elem);
|
||||
last_elem = NULL;
|
||||
}
|
||||
|
||||
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
|
||||
len = readlink(buf, next_buf, PATH_MAX);
|
||||
if (len < 0)
|
||||
die ("Invalid symlink: %s", buf);
|
||||
if (PATH_MAX <= len)
|
||||
die("symbolic link too long: %s", buf);
|
||||
next_buf[len] = '\0';
|
||||
buf = next_buf;
|
||||
buf_index = 1 - buf_index;
|
||||
next_buf = bufs[buf_index];
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (*cwd && chdir(cwd))
|
||||
die ("Could not change back to '%s'", cwd);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *get_pwd_cwd(void)
|
||||
{
|
||||
static char cwd[PATH_MAX + 1];
|
||||
char *pwd;
|
||||
struct stat cwd_stat, pwd_stat;
|
||||
if (getcwd(cwd, PATH_MAX) == NULL)
|
||||
return NULL;
|
||||
pwd = getenv("PWD");
|
||||
if (pwd && strcmp(pwd, cwd)) {
|
||||
stat(cwd, &cwd_stat);
|
||||
if (!stat(pwd, &pwd_stat) &&
|
||||
pwd_stat.st_dev == cwd_stat.st_dev &&
|
||||
pwd_stat.st_ino == cwd_stat.st_ino) {
|
||||
strlcpy(cwd, pwd, PATH_MAX);
|
||||
}
|
||||
}
|
||||
return cwd;
|
||||
}
|
||||
|
||||
const char *make_nonrelative_path(const char *path)
|
||||
{
|
||||
static char buf[PATH_MAX + 1];
|
||||
|
||||
if (is_absolute_path(path)) {
|
||||
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
|
||||
die("Too long path: %.*s", 60, path);
|
||||
} else {
|
||||
const char *cwd = get_pwd_cwd();
|
||||
if (!cwd)
|
||||
die("Cannot determine the current working directory");
|
||||
if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
|
||||
die("Too long path: %.*s", 60, path);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "cache.h"
|
||||
|
||||
static const char *alias_key;
|
||||
static char *alias_val;
|
||||
|
||||
static int alias_lookup_cb(const char *k, const char *v, void *cb)
|
||||
{
|
||||
if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
|
||||
if (!v)
|
||||
return config_error_nonbool(k);
|
||||
alias_val = strdup(v);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *alias_lookup(const char *alias)
|
||||
{
|
||||
alias_key = alias;
|
||||
alias_val = NULL;
|
||||
perf_config(alias_lookup_cb, NULL);
|
||||
return alias_val;
|
||||
}
|
||||
|
||||
int split_cmdline(char *cmdline, const char ***argv)
|
||||
{
|
||||
int src, dst, count = 0, size = 16;
|
||||
char quoted = 0;
|
||||
|
||||
*argv = malloc(sizeof(char*) * size);
|
||||
|
||||
/* split alias_string */
|
||||
(*argv)[count++] = cmdline;
|
||||
for (src = dst = 0; cmdline[src];) {
|
||||
char c = cmdline[src];
|
||||
if (!quoted && isspace(c)) {
|
||||
cmdline[dst++] = 0;
|
||||
while (cmdline[++src]
|
||||
&& isspace(cmdline[src]))
|
||||
; /* skip */
|
||||
if (count >= size) {
|
||||
size += 16;
|
||||
*argv = realloc(*argv, sizeof(char*) * size);
|
||||
}
|
||||
(*argv)[count++] = cmdline + dst;
|
||||
} else if (!quoted && (c == '\'' || c == '"')) {
|
||||
quoted = c;
|
||||
src++;
|
||||
} else if (c == quoted) {
|
||||
quoted = 0;
|
||||
src++;
|
||||
} else {
|
||||
if (c == '\\' && quoted != '\'') {
|
||||
src++;
|
||||
c = cmdline[src];
|
||||
if (!c) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("cmdline ends with \\");
|
||||
}
|
||||
}
|
||||
cmdline[dst++] = c;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
cmdline[dst] = 0;
|
||||
|
||||
if (quoted) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("unclosed quote");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* builtin-help.c
|
||||
*
|
||||
* Builtin help command
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "common-cmds.h"
|
||||
#include "parse-options.h"
|
||||
#include "run-command.h"
|
||||
#include "help.h"
|
||||
|
||||
static struct man_viewer_list {
|
||||
struct man_viewer_list *next;
|
||||
char name[FLEX_ARRAY];
|
||||
} *man_viewer_list;
|
||||
|
||||
static struct man_viewer_info_list {
|
||||
struct man_viewer_info_list *next;
|
||||
const char *info;
|
||||
char name[FLEX_ARRAY];
|
||||
} *man_viewer_info_list;
|
||||
|
||||
enum help_format {
|
||||
HELP_FORMAT_MAN,
|
||||
HELP_FORMAT_INFO,
|
||||
HELP_FORMAT_WEB,
|
||||
};
|
||||
|
||||
static int show_all = 0;
|
||||
static enum help_format help_format = HELP_FORMAT_MAN;
|
||||
static struct option builtin_help_options[] = {
|
||||
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
|
||||
OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
|
||||
OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
|
||||
HELP_FORMAT_WEB),
|
||||
OPT_SET_INT('i', "info", &help_format, "show info page",
|
||||
HELP_FORMAT_INFO),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
static const char * const builtin_help_usage[] = {
|
||||
"perf help [--all] [--man|--web|--info] [command]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static enum help_format parse_help_format(const char *format)
|
||||
{
|
||||
if (!strcmp(format, "man"))
|
||||
return HELP_FORMAT_MAN;
|
||||
if (!strcmp(format, "info"))
|
||||
return HELP_FORMAT_INFO;
|
||||
if (!strcmp(format, "web") || !strcmp(format, "html"))
|
||||
return HELP_FORMAT_WEB;
|
||||
die("unrecognized help format '%s'", format);
|
||||
}
|
||||
|
||||
static const char *get_man_viewer_info(const char *name)
|
||||
{
|
||||
struct man_viewer_info_list *viewer;
|
||||
|
||||
for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
|
||||
{
|
||||
if (!strcasecmp(name, viewer->name))
|
||||
return viewer->info;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int check_emacsclient_version(void)
|
||||
{
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
struct child_process ec_process;
|
||||
const char *argv_ec[] = { "emacsclient", "--version", NULL };
|
||||
int version;
|
||||
|
||||
/* emacsclient prints its version number on stderr */
|
||||
memset(&ec_process, 0, sizeof(ec_process));
|
||||
ec_process.argv = argv_ec;
|
||||
ec_process.err = -1;
|
||||
ec_process.stdout_to_stderr = 1;
|
||||
if (start_command(&ec_process)) {
|
||||
fprintf(stderr, "Failed to start emacsclient.\n");
|
||||
return -1;
|
||||
}
|
||||
strbuf_read(&buffer, ec_process.err, 20);
|
||||
close(ec_process.err);
|
||||
|
||||
/*
|
||||
* Don't bother checking return value, because "emacsclient --version"
|
||||
* seems to always exits with code 1.
|
||||
*/
|
||||
finish_command(&ec_process);
|
||||
|
||||
if (prefixcmp(buffer.buf, "emacsclient")) {
|
||||
fprintf(stderr, "Failed to parse emacsclient version.\n");
|
||||
strbuf_release(&buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strbuf_remove(&buffer, 0, strlen("emacsclient"));
|
||||
version = atoi(buffer.buf);
|
||||
|
||||
if (version < 22) {
|
||||
fprintf(stderr,
|
||||
"emacsclient version '%d' too old (< 22).\n",
|
||||
version);
|
||||
strbuf_release(&buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strbuf_release(&buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exec_woman_emacs(const char* path, const char *page)
|
||||
{
|
||||
if (!check_emacsclient_version()) {
|
||||
/* This works only with emacsclient version >= 22. */
|
||||
struct strbuf man_page = STRBUF_INIT;
|
||||
|
||||
if (!path)
|
||||
path = "emacsclient";
|
||||
strbuf_addf(&man_page, "(woman \"%s\")", page);
|
||||
execlp(path, "emacsclient", "-e", man_page.buf, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void exec_man_konqueror(const char* path, const char *page)
|
||||
{
|
||||
const char *display = getenv("DISPLAY");
|
||||
if (display && *display) {
|
||||
struct strbuf man_page = STRBUF_INIT;
|
||||
const char *filename = "kfmclient";
|
||||
|
||||
/* It's simpler to launch konqueror using kfmclient. */
|
||||
if (path) {
|
||||
const char *file = strrchr(path, '/');
|
||||
if (file && !strcmp(file + 1, "konqueror")) {
|
||||
char *new = strdup(path);
|
||||
char *dest = strrchr(new, '/');
|
||||
|
||||
/* strlen("konqueror") == strlen("kfmclient") */
|
||||
strcpy(dest + 1, "kfmclient");
|
||||
path = new;
|
||||
}
|
||||
if (file)
|
||||
filename = file;
|
||||
} else
|
||||
path = "kfmclient";
|
||||
strbuf_addf(&man_page, "man:%s(1)", page);
|
||||
execlp(path, filename, "newTab", man_page.buf, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void exec_man_man(const char* path, const char *page)
|
||||
{
|
||||
if (!path)
|
||||
path = "man";
|
||||
execlp(path, "man", page, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
}
|
||||
|
||||
static void exec_man_cmd(const char *cmd, const char *page)
|
||||
{
|
||||
struct strbuf shell_cmd = STRBUF_INIT;
|
||||
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
|
||||
execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
|
||||
warning("failed to exec '%s': %s", cmd, strerror(errno));
|
||||
}
|
||||
|
||||
static void add_man_viewer(const char *name)
|
||||
{
|
||||
struct man_viewer_list **p = &man_viewer_list;
|
||||
size_t len = strlen(name);
|
||||
|
||||
while (*p)
|
||||
p = &((*p)->next);
|
||||
*p = calloc(1, (sizeof(**p) + len + 1));
|
||||
strncpy((*p)->name, name, len);
|
||||
}
|
||||
|
||||
static int supported_man_viewer(const char *name, size_t len)
|
||||
{
|
||||
return (!strncasecmp("man", name, len) ||
|
||||
!strncasecmp("woman", name, len) ||
|
||||
!strncasecmp("konqueror", name, len));
|
||||
}
|
||||
|
||||
static void do_add_man_viewer_info(const char *name,
|
||||
size_t len,
|
||||
const char *value)
|
||||
{
|
||||
struct man_viewer_info_list *new = calloc(1, sizeof(*new) + len + 1);
|
||||
|
||||
strncpy(new->name, name, len);
|
||||
new->info = strdup(value);
|
||||
new->next = man_viewer_info_list;
|
||||
man_viewer_info_list = new;
|
||||
}
|
||||
|
||||
static int add_man_viewer_path(const char *name,
|
||||
size_t len,
|
||||
const char *value)
|
||||
{
|
||||
if (supported_man_viewer(name, len))
|
||||
do_add_man_viewer_info(name, len, value);
|
||||
else
|
||||
warning("'%s': path for unsupported man viewer.\n"
|
||||
"Please consider using 'man.<tool>.cmd' instead.",
|
||||
name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_man_viewer_cmd(const char *name,
|
||||
size_t len,
|
||||
const char *value)
|
||||
{
|
||||
if (supported_man_viewer(name, len))
|
||||
warning("'%s': cmd for supported man viewer.\n"
|
||||
"Please consider using 'man.<tool>.path' instead.",
|
||||
name);
|
||||
else
|
||||
do_add_man_viewer_info(name, len, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_man_viewer_info(const char *var, const char *value)
|
||||
{
|
||||
const char *name = var + 4;
|
||||
const char *subkey = strrchr(name, '.');
|
||||
|
||||
if (!subkey)
|
||||
return error("Config with no key for man viewer: %s", name);
|
||||
|
||||
if (!strcmp(subkey, ".path")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
return add_man_viewer_path(name, subkey - name, value);
|
||||
}
|
||||
if (!strcmp(subkey, ".cmd")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
return add_man_viewer_cmd(name, subkey - name, value);
|
||||
}
|
||||
|
||||
warning("'%s': unsupported man viewer sub key.", subkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_help_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "help.format")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
help_format = parse_help_format(value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "man.viewer")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
add_man_viewer(value);
|
||||
return 0;
|
||||
}
|
||||
if (!prefixcmp(var, "man."))
|
||||
return add_man_viewer_info(var, value);
|
||||
|
||||
return perf_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
static struct cmdnames main_cmds, other_cmds;
|
||||
|
||||
void list_common_cmds_help(void)
|
||||
{
|
||||
int i, longest = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
if (longest < strlen(common_cmds[i].name))
|
||||
longest = strlen(common_cmds[i].name);
|
||||
}
|
||||
|
||||
puts("The most commonly used perf commands are:");
|
||||
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
printf(" %s ", common_cmds[i].name);
|
||||
mput_char(' ', longest - strlen(common_cmds[i].name));
|
||||
puts(common_cmds[i].help);
|
||||
}
|
||||
}
|
||||
|
||||
static int is_perf_command(const char *s)
|
||||
{
|
||||
return is_in_cmdlist(&main_cmds, s) ||
|
||||
is_in_cmdlist(&other_cmds, s);
|
||||
}
|
||||
|
||||
static const char *prepend(const char *prefix, const char *cmd)
|
||||
{
|
||||
size_t pre_len = strlen(prefix);
|
||||
size_t cmd_len = strlen(cmd);
|
||||
char *p = malloc(pre_len + cmd_len + 1);
|
||||
memcpy(p, prefix, pre_len);
|
||||
strcpy(p + pre_len, cmd);
|
||||
return p;
|
||||
}
|
||||
|
||||
static const char *cmd_to_page(const char *perf_cmd)
|
||||
{
|
||||
if (!perf_cmd)
|
||||
return "perf";
|
||||
else if (!prefixcmp(perf_cmd, "perf"))
|
||||
return perf_cmd;
|
||||
else if (is_perf_command(perf_cmd))
|
||||
return prepend("perf-", perf_cmd);
|
||||
else
|
||||
return prepend("perf", perf_cmd);
|
||||
}
|
||||
|
||||
static void setup_man_path(void)
|
||||
{
|
||||
struct strbuf new_path = STRBUF_INIT;
|
||||
const char *old_path = getenv("MANPATH");
|
||||
|
||||
/* We should always put ':' after our path. If there is no
|
||||
* old_path, the ':' at the end will let 'man' to try
|
||||
* system-wide paths after ours to find the manual page. If
|
||||
* there is old_path, we need ':' as delimiter. */
|
||||
strbuf_addstr(&new_path, system_path(PERF_MAN_PATH));
|
||||
strbuf_addch(&new_path, ':');
|
||||
if (old_path)
|
||||
strbuf_addstr(&new_path, old_path);
|
||||
|
||||
setenv("MANPATH", new_path.buf, 1);
|
||||
|
||||
strbuf_release(&new_path);
|
||||
}
|
||||
|
||||
static void exec_viewer(const char *name, const char *page)
|
||||
{
|
||||
const char *info = get_man_viewer_info(name);
|
||||
|
||||
if (!strcasecmp(name, "man"))
|
||||
exec_man_man(info, page);
|
||||
else if (!strcasecmp(name, "woman"))
|
||||
exec_woman_emacs(info, page);
|
||||
else if (!strcasecmp(name, "konqueror"))
|
||||
exec_man_konqueror(info, page);
|
||||
else if (info)
|
||||
exec_man_cmd(info, page);
|
||||
else
|
||||
warning("'%s': unknown man viewer.", name);
|
||||
}
|
||||
|
||||
static void show_man_page(const char *perf_cmd)
|
||||
{
|
||||
struct man_viewer_list *viewer;
|
||||
const char *page = cmd_to_page(perf_cmd);
|
||||
const char *fallback = getenv("PERF_MAN_VIEWER");
|
||||
|
||||
setup_man_path();
|
||||
for (viewer = man_viewer_list; viewer; viewer = viewer->next)
|
||||
{
|
||||
exec_viewer(viewer->name, page); /* will return when unable */
|
||||
}
|
||||
if (fallback)
|
||||
exec_viewer(fallback, page);
|
||||
exec_viewer("man", page);
|
||||
die("no man viewer handled the request");
|
||||
}
|
||||
|
||||
static void show_info_page(const char *perf_cmd)
|
||||
{
|
||||
const char *page = cmd_to_page(perf_cmd);
|
||||
setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
|
||||
execlp("info", "info", "perfman", page, NULL);
|
||||
}
|
||||
|
||||
static void get_html_page_path(struct strbuf *page_path, const char *page)
|
||||
{
|
||||
struct stat st;
|
||||
const char *html_path = system_path(PERF_HTML_PATH);
|
||||
|
||||
/* Check that we have a perf documentation directory. */
|
||||
if (stat(mkpath("%s/perf.html", html_path), &st)
|
||||
|| !S_ISREG(st.st_mode))
|
||||
die("'%s': not a documentation directory.", html_path);
|
||||
|
||||
strbuf_init(page_path, 0);
|
||||
strbuf_addf(page_path, "%s/%s.html", html_path, page);
|
||||
}
|
||||
|
||||
/*
|
||||
* If open_html is not defined in a platform-specific way (see for
|
||||
* example compat/mingw.h), we use the script web--browse to display
|
||||
* HTML.
|
||||
*/
|
||||
#ifndef open_html
|
||||
void open_html(const char *path)
|
||||
{
|
||||
execl_perf_cmd("web--browse", "-c", "help.browser", path, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void show_html_page(const char *perf_cmd)
|
||||
{
|
||||
const char *page = cmd_to_page(perf_cmd);
|
||||
struct strbuf page_path; /* it leaks but we exec bellow */
|
||||
|
||||
get_html_page_path(&page_path, page);
|
||||
|
||||
open_html(page_path.buf);
|
||||
}
|
||||
|
||||
int cmd_help(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int nonperf;
|
||||
const char *alias;
|
||||
load_command_list("perf-", &main_cmds, &other_cmds);
|
||||
|
||||
/* setup_perf_directory_gently(&nonperf); */
|
||||
perf_config(perf_help_config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, builtin_help_options,
|
||||
builtin_help_usage, 0);
|
||||
|
||||
if (show_all) {
|
||||
printf("usage: %s\n\n", perf_usage_string);
|
||||
list_commands("perf commands", &main_cmds, &other_cmds);
|
||||
printf("%s\n", perf_more_info_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!argv[0]) {
|
||||
printf("usage: %s\n\n", perf_usage_string);
|
||||
list_common_cmds_help();
|
||||
printf("\n%s\n", perf_more_info_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
alias = alias_lookup(argv[0]);
|
||||
if (alias && !is_perf_command(argv[0])) {
|
||||
printf("`perf %s' is aliased to `%s'\n", argv[0], alias);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (help_format) {
|
||||
case HELP_FORMAT_MAN:
|
||||
show_man_page(argv[0]);
|
||||
break;
|
||||
case HELP_FORMAT_INFO:
|
||||
show_info_page(argv[0]);
|
||||
break;
|
||||
case HELP_FORMAT_WEB:
|
||||
show_html_page(argv[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
#ifndef BUILTIN_H
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "util.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
extern const char perf_version_string[];
|
||||
extern const char perf_usage_string[];
|
||||
extern const char perf_more_info_string[];
|
||||
|
||||
extern void list_common_cmds_help(void);
|
||||
extern const char *help_unknown_cmd(const char *cmd);
|
||||
extern void prune_packed_objects(int);
|
||||
extern int read_line_with_nul(char *buf, int size, FILE *file);
|
||||
extern int check_pager_config(const char *cmd);
|
||||
|
||||
extern int cmd_top(int argc, const char **argv, const char *prefix);
|
||||
#endif
|
||||
@@ -0,0 +1,97 @@
|
||||
#ifndef CACHE_H
|
||||
#define CACHE_H
|
||||
|
||||
#include "util.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
#define PERF_DIR_ENVIRONMENT "PERF_DIR"
|
||||
#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
|
||||
#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
|
||||
#define DB_ENVIRONMENT "PERF_OBJECT_DIRECTORY"
|
||||
#define INDEX_ENVIRONMENT "PERF_INDEX_FILE"
|
||||
#define GRAFT_ENVIRONMENT "PERF_GRAFT_FILE"
|
||||
#define TEMPLATE_DIR_ENVIRONMENT "PERF_TEMPLATE_DIR"
|
||||
#define CONFIG_ENVIRONMENT "PERF_CONFIG"
|
||||
#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
|
||||
#define CEILING_DIRECTORIES_ENVIRONMENT "PERF_CEILING_DIRECTORIES"
|
||||
#define PERFATTRIBUTES_FILE ".perfattributes"
|
||||
#define INFOATTRIBUTES_FILE "info/attributes"
|
||||
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
|
||||
|
||||
typedef int (*config_fn_t)(const char *, const char *, void *);
|
||||
extern int perf_default_config(const char *, const char *, void *);
|
||||
extern int perf_config_from_file(config_fn_t fn, const char *, void *);
|
||||
extern int perf_config(config_fn_t fn, void *);
|
||||
extern int perf_parse_ulong(const char *, unsigned long *);
|
||||
extern int perf_config_int(const char *, const char *);
|
||||
extern unsigned long perf_config_ulong(const char *, const char *);
|
||||
extern int perf_config_bool_or_int(const char *, const char *, int *);
|
||||
extern int perf_config_bool(const char *, const char *);
|
||||
extern int perf_config_string(const char **, const char *, const char *);
|
||||
extern int perf_config_set(const char *, const char *);
|
||||
extern int perf_config_set_multivar(const char *, const char *, const char *, int);
|
||||
extern int perf_config_rename_section(const char *, const char *);
|
||||
extern const char *perf_etc_perfconfig(void);
|
||||
extern int check_repository_format_version(const char *var, const char *value, void *cb);
|
||||
extern int perf_config_system(void);
|
||||
extern int perf_config_global(void);
|
||||
extern int config_error_nonbool(const char *);
|
||||
extern const char *config_exclusive_filename;
|
||||
|
||||
#define MAX_PERFNAME (1000)
|
||||
extern char perf_default_email[MAX_PERFNAME];
|
||||
extern char perf_default_name[MAX_PERFNAME];
|
||||
extern int user_ident_explicitly_given;
|
||||
|
||||
extern const char *perf_log_output_encoding;
|
||||
extern const char *perf_mailmap_file;
|
||||
|
||||
/* IO helper functions */
|
||||
extern void maybe_flush_or_die(FILE *, const char *);
|
||||
extern int copy_fd(int ifd, int ofd);
|
||||
extern int copy_file(const char *dst, const char *src, int mode);
|
||||
extern ssize_t read_in_full(int fd, void *buf, size_t count);
|
||||
extern ssize_t write_in_full(int fd, const void *buf, size_t count);
|
||||
extern void write_or_die(int fd, const void *buf, size_t count);
|
||||
extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
|
||||
extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
|
||||
extern void fsync_or_die(int fd, const char *);
|
||||
|
||||
/* pager.c */
|
||||
extern void setup_pager(void);
|
||||
extern const char *pager_program;
|
||||
extern int pager_in_use(void);
|
||||
extern int pager_use_color;
|
||||
|
||||
extern const char *editor_program;
|
||||
extern const char *excludes_file;
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
int split_cmdline(char *cmdline, const char ***argv);
|
||||
|
||||
#define alloc_nr(x) (((x)+16)*3/2)
|
||||
|
||||
/*
|
||||
* Realloc the buffer pointed at by variable 'x' so that it can hold
|
||||
* at least 'nr' entries; the number of entries currently allocated
|
||||
* is 'alloc', using the standard growing factor alloc_nr() macro.
|
||||
*
|
||||
* DO NOT USE any expression with side-effect for 'x' or 'alloc'.
|
||||
*/
|
||||
#define ALLOC_GROW(x, nr, alloc) \
|
||||
do { \
|
||||
if ((nr) > alloc) { \
|
||||
if (alloc_nr(alloc) < (nr)) \
|
||||
alloc = (nr); \
|
||||
else \
|
||||
alloc = alloc_nr(alloc); \
|
||||
x = xrealloc((x), alloc * sizeof(*(x))); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
static inline int is_absolute_path(const char *path)
|
||||
{
|
||||
return path[0] == '/';
|
||||
}
|
||||
#endif /* CACHE_H */
|
||||
@@ -0,0 +1,4 @@
|
||||
# List of known perf commands.
|
||||
# command name category [deprecated] [common]
|
||||
perf-top mainporcelain common
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Sane locale-independent, ASCII ctype.
|
||||
*
|
||||
* No surprises, and works with signed and unsigned chars.
|
||||
*/
|
||||
#include "cache.h"
|
||||
|
||||
enum {
|
||||
S = GIT_SPACE,
|
||||
A = GIT_ALPHA,
|
||||
D = GIT_DIGIT,
|
||||
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
|
||||
R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */
|
||||
};
|
||||
|
||||
unsigned char sane_ctype[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
|
||||
S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0, /* 32.. 47 */
|
||||
D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G, /* 48.. 63 */
|
||||
0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
|
||||
A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0, /* 80.. 95 */
|
||||
0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
|
||||
A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0, /* 112..127 */
|
||||
/* Nothing in the 128.. range */
|
||||
};
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "cache.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "quote.h"
|
||||
#define MAX_ARGS 32
|
||||
|
||||
extern char **environ;
|
||||
static const char *argv_exec_path;
|
||||
static const char *argv0_path;
|
||||
|
||||
const char *system_path(const char *path)
|
||||
{
|
||||
#ifdef RUNTIME_PREFIX
|
||||
static const char *prefix;
|
||||
#else
|
||||
static const char *prefix = PREFIX;
|
||||
#endif
|
||||
struct strbuf d = STRBUF_INIT;
|
||||
|
||||
if (is_absolute_path(path))
|
||||
return path;
|
||||
|
||||
#ifdef RUNTIME_PREFIX
|
||||
assert(argv0_path);
|
||||
assert(is_absolute_path(argv0_path));
|
||||
|
||||
if (!prefix &&
|
||||
!(prefix = strip_path_suffix(argv0_path, PERF_EXEC_PATH)) &&
|
||||
!(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
|
||||
!(prefix = strip_path_suffix(argv0_path, "perf"))) {
|
||||
prefix = PREFIX;
|
||||
fprintf(stderr, "RUNTIME_PREFIX requested, "
|
||||
"but prefix computation failed. "
|
||||
"Using static fallback '%s'.\n", prefix);
|
||||
}
|
||||
#endif
|
||||
|
||||
strbuf_addf(&d, "%s/%s", prefix, path);
|
||||
path = strbuf_detach(&d, NULL);
|
||||
return path;
|
||||
}
|
||||
|
||||
const char *perf_extract_argv0_path(const char *argv0)
|
||||
{
|
||||
const char *slash;
|
||||
|
||||
if (!argv0 || !*argv0)
|
||||
return NULL;
|
||||
slash = argv0 + strlen(argv0);
|
||||
|
||||
while (argv0 <= slash && !is_dir_sep(*slash))
|
||||
slash--;
|
||||
|
||||
if (slash >= argv0) {
|
||||
argv0_path = strndup(argv0, slash - argv0);
|
||||
return slash + 1;
|
||||
}
|
||||
|
||||
return argv0;
|
||||
}
|
||||
|
||||
void perf_set_argv_exec_path(const char *exec_path)
|
||||
{
|
||||
argv_exec_path = exec_path;
|
||||
/*
|
||||
* Propagate this setting to external programs.
|
||||
*/
|
||||
setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1);
|
||||
}
|
||||
|
||||
|
||||
/* Returns the highest-priority, location to look for perf programs. */
|
||||
const char *perf_exec_path(void)
|
||||
{
|
||||
const char *env;
|
||||
|
||||
if (argv_exec_path)
|
||||
return argv_exec_path;
|
||||
|
||||
env = getenv(EXEC_PATH_ENVIRONMENT);
|
||||
if (env && *env) {
|
||||
return env;
|
||||
}
|
||||
|
||||
return system_path(PERF_EXEC_PATH);
|
||||
}
|
||||
|
||||
static void add_path(struct strbuf *out, const char *path)
|
||||
{
|
||||
if (path && *path) {
|
||||
if (is_absolute_path(path))
|
||||
strbuf_addstr(out, path);
|
||||
else
|
||||
strbuf_addstr(out, make_nonrelative_path(path));
|
||||
|
||||
strbuf_addch(out, PATH_SEP);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_path(void)
|
||||
{
|
||||
const char *old_path = getenv("PATH");
|
||||
struct strbuf new_path = STRBUF_INIT;
|
||||
|
||||
add_path(&new_path, perf_exec_path());
|
||||
add_path(&new_path, argv0_path);
|
||||
|
||||
if (old_path)
|
||||
strbuf_addstr(&new_path, old_path);
|
||||
else
|
||||
strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
|
||||
|
||||
setenv("PATH", new_path.buf, 1);
|
||||
|
||||
strbuf_release(&new_path);
|
||||
}
|
||||
|
||||
const char **prepare_perf_cmd(const char **argv)
|
||||
{
|
||||
int argc;
|
||||
const char **nargv;
|
||||
|
||||
for (argc = 0; argv[argc]; argc++)
|
||||
; /* just counting */
|
||||
nargv = malloc(sizeof(*nargv) * (argc + 2));
|
||||
|
||||
nargv[0] = "perf";
|
||||
for (argc = 0; argv[argc]; argc++)
|
||||
nargv[argc + 1] = argv[argc];
|
||||
nargv[argc + 1] = NULL;
|
||||
return nargv;
|
||||
}
|
||||
|
||||
int execv_perf_cmd(const char **argv) {
|
||||
const char **nargv = prepare_perf_cmd(argv);
|
||||
|
||||
/* execvp() can only ever return if it fails */
|
||||
execvp("perf", (char **)nargv);
|
||||
|
||||
free(nargv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int execl_perf_cmd(const char *cmd,...)
|
||||
{
|
||||
int argc;
|
||||
const char *argv[MAX_ARGS + 1];
|
||||
const char *arg;
|
||||
va_list param;
|
||||
|
||||
va_start(param, cmd);
|
||||
argv[0] = cmd;
|
||||
argc = 1;
|
||||
while (argc < MAX_ARGS) {
|
||||
arg = argv[argc++] = va_arg(param, char *);
|
||||
if (!arg)
|
||||
break;
|
||||
}
|
||||
va_end(param);
|
||||
if (MAX_ARGS <= argc)
|
||||
return error("too many args to run %s", cmd);
|
||||
|
||||
argv[argc] = NULL;
|
||||
return execv_perf_cmd(argv);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef PERF_EXEC_CMD_H
|
||||
#define PERF_EXEC_CMD_H
|
||||
|
||||
extern void perf_set_argv_exec_path(const char *exec_path);
|
||||
extern const char *perf_extract_argv0_path(const char *path);
|
||||
extern const char *perf_exec_path(void);
|
||||
extern void setup_path(void);
|
||||
extern const char **prepare_perf_cmd(const char **argv);
|
||||
extern int execv_perf_cmd(const char **argv); /* NULL terminated */
|
||||
extern int execl_perf_cmd(const char *cmd, ...);
|
||||
extern const char *system_path(const char *path);
|
||||
|
||||
#endif /* PERF_EXEC_CMD_H */
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "/* Automatically generated by $0 */
|
||||
struct cmdname_help
|
||||
{
|
||||
char name[16];
|
||||
char help[80];
|
||||
};
|
||||
|
||||
static struct cmdname_help common_cmds[] = {"
|
||||
|
||||
sed -n -e 's/^git-\([^ ]*\)[ ].* common.*/\1/p' command-list.txt |
|
||||
sort |
|
||||
while read cmd
|
||||
do
|
||||
sed -n '
|
||||
/^NAME/,/git-'"$cmd"'/H
|
||||
${
|
||||
x
|
||||
s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
|
||||
p
|
||||
}' "Documentation/git-$cmd.txt"
|
||||
done
|
||||
echo "};"
|
||||
@@ -0,0 +1,366 @@
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "levenshtein.h"
|
||||
#include "help.h"
|
||||
|
||||
/* most GUI terminals set COLUMNS (although some don't export it) */
|
||||
static int term_columns(void)
|
||||
{
|
||||
char *col_string = getenv("COLUMNS");
|
||||
int n_cols;
|
||||
|
||||
if (col_string && (n_cols = atoi(col_string)) > 0)
|
||||
return n_cols;
|
||||
|
||||
#ifdef TIOCGWINSZ
|
||||
{
|
||||
struct winsize ws;
|
||||
if (!ioctl(1, TIOCGWINSZ, &ws)) {
|
||||
if (ws.ws_col)
|
||||
return ws.ws_col;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 80;
|
||||
}
|
||||
|
||||
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
|
||||
{
|
||||
struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
|
||||
|
||||
ent->len = len;
|
||||
memcpy(ent->name, name, len);
|
||||
ent->name[len] = 0;
|
||||
|
||||
ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
|
||||
cmds->names[cmds->cnt++] = ent;
|
||||
}
|
||||
|
||||
static void clean_cmdnames(struct cmdnames *cmds)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < cmds->cnt; ++i)
|
||||
free(cmds->names[i]);
|
||||
free(cmds->names);
|
||||
cmds->cnt = 0;
|
||||
cmds->alloc = 0;
|
||||
}
|
||||
|
||||
static int cmdname_compare(const void *a_, const void *b_)
|
||||
{
|
||||
struct cmdname *a = *(struct cmdname **)a_;
|
||||
struct cmdname *b = *(struct cmdname **)b_;
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
static void uniq(struct cmdnames *cmds)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (!cmds->cnt)
|
||||
return;
|
||||
|
||||
for (i = j = 1; i < cmds->cnt; i++)
|
||||
if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
|
||||
cmds->names[j++] = cmds->names[i];
|
||||
|
||||
cmds->cnt = j;
|
||||
}
|
||||
|
||||
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
|
||||
{
|
||||
int ci, cj, ei;
|
||||
int cmp;
|
||||
|
||||
ci = cj = ei = 0;
|
||||
while (ci < cmds->cnt && ei < excludes->cnt) {
|
||||
cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
|
||||
if (cmp < 0)
|
||||
cmds->names[cj++] = cmds->names[ci++];
|
||||
else if (cmp == 0)
|
||||
ci++, ei++;
|
||||
else if (cmp > 0)
|
||||
ei++;
|
||||
}
|
||||
|
||||
while (ci < cmds->cnt)
|
||||
cmds->names[cj++] = cmds->names[ci++];
|
||||
|
||||
cmds->cnt = cj;
|
||||
}
|
||||
|
||||
static void pretty_print_string_list(struct cmdnames *cmds, int longest)
|
||||
{
|
||||
int cols = 1, rows;
|
||||
int space = longest + 1; /* min 1 SP between words */
|
||||
int max_cols = term_columns() - 1; /* don't print *on* the edge */
|
||||
int i, j;
|
||||
|
||||
if (space < max_cols)
|
||||
cols = max_cols / space;
|
||||
rows = (cmds->cnt + cols - 1) / cols;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
printf(" ");
|
||||
|
||||
for (j = 0; j < cols; j++) {
|
||||
int n = j * rows + i;
|
||||
int size = space;
|
||||
if (n >= cmds->cnt)
|
||||
break;
|
||||
if (j == cols-1 || n + rows >= cmds->cnt)
|
||||
size = 1;
|
||||
printf("%-*s", size, cmds->names[n]->name);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
static int is_executable(const char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(name, &st) || /* stat, not lstat */
|
||||
!S_ISREG(st.st_mode))
|
||||
return 0;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
/* cannot trust the executable bit, peek into the file instead */
|
||||
char buf[3] = { 0 };
|
||||
int n;
|
||||
int fd = open(name, O_RDONLY);
|
||||
st.st_mode &= ~S_IXUSR;
|
||||
if (fd >= 0) {
|
||||
n = read(fd, buf, 2);
|
||||
if (n == 2)
|
||||
/* DOS executables start with "MZ" */
|
||||
if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
|
||||
st.st_mode |= S_IXUSR;
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
return st.st_mode & S_IXUSR;
|
||||
}
|
||||
|
||||
static void list_commands_in_dir(struct cmdnames *cmds,
|
||||
const char *path,
|
||||
const char *prefix)
|
||||
{
|
||||
int prefix_len;
|
||||
DIR *dir = opendir(path);
|
||||
struct dirent *de;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int len;
|
||||
|
||||
if (!dir)
|
||||
return;
|
||||
if (!prefix)
|
||||
prefix = "perf-";
|
||||
prefix_len = strlen(prefix);
|
||||
|
||||
strbuf_addf(&buf, "%s/", path);
|
||||
len = buf.len;
|
||||
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
int entlen;
|
||||
|
||||
if (prefixcmp(de->d_name, prefix))
|
||||
continue;
|
||||
|
||||
strbuf_setlen(&buf, len);
|
||||
strbuf_addstr(&buf, de->d_name);
|
||||
if (!is_executable(buf.buf))
|
||||
continue;
|
||||
|
||||
entlen = strlen(de->d_name) - prefix_len;
|
||||
if (has_extension(de->d_name, ".exe"))
|
||||
entlen -= 4;
|
||||
|
||||
add_cmdname(cmds, de->d_name + prefix_len, entlen);
|
||||
}
|
||||
closedir(dir);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
void load_command_list(const char *prefix,
|
||||
struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds)
|
||||
{
|
||||
const char *env_path = getenv("PATH");
|
||||
const char *exec_path = perf_exec_path();
|
||||
|
||||
if (exec_path) {
|
||||
list_commands_in_dir(main_cmds, exec_path, prefix);
|
||||
qsort(main_cmds->names, main_cmds->cnt,
|
||||
sizeof(*main_cmds->names), cmdname_compare);
|
||||
uniq(main_cmds);
|
||||
}
|
||||
|
||||
if (env_path) {
|
||||
char *paths, *path, *colon;
|
||||
path = paths = strdup(env_path);
|
||||
while (1) {
|
||||
if ((colon = strchr(path, PATH_SEP)))
|
||||
*colon = 0;
|
||||
if (!exec_path || strcmp(path, exec_path))
|
||||
list_commands_in_dir(other_cmds, path, prefix);
|
||||
|
||||
if (!colon)
|
||||
break;
|
||||
path = colon + 1;
|
||||
}
|
||||
free(paths);
|
||||
|
||||
qsort(other_cmds->names, other_cmds->cnt,
|
||||
sizeof(*other_cmds->names), cmdname_compare);
|
||||
uniq(other_cmds);
|
||||
}
|
||||
exclude_cmds(other_cmds, main_cmds);
|
||||
}
|
||||
|
||||
void list_commands(const char *title, struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds)
|
||||
{
|
||||
int i, longest = 0;
|
||||
|
||||
for (i = 0; i < main_cmds->cnt; i++)
|
||||
if (longest < main_cmds->names[i]->len)
|
||||
longest = main_cmds->names[i]->len;
|
||||
for (i = 0; i < other_cmds->cnt; i++)
|
||||
if (longest < other_cmds->names[i]->len)
|
||||
longest = other_cmds->names[i]->len;
|
||||
|
||||
if (main_cmds->cnt) {
|
||||
const char *exec_path = perf_exec_path();
|
||||
printf("available %s in '%s'\n", title, exec_path);
|
||||
printf("----------------");
|
||||
mput_char('-', strlen(title) + strlen(exec_path));
|
||||
putchar('\n');
|
||||
pretty_print_string_list(main_cmds, longest);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
if (other_cmds->cnt) {
|
||||
printf("%s available from elsewhere on your $PATH\n", title);
|
||||
printf("---------------------------------------");
|
||||
mput_char('-', strlen(title));
|
||||
putchar('\n');
|
||||
pretty_print_string_list(other_cmds, longest);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
int is_in_cmdlist(struct cmdnames *c, const char *s)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < c->cnt; i++)
|
||||
if (!strcmp(s, c->names[i]->name))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autocorrect;
|
||||
static struct cmdnames aliases;
|
||||
|
||||
static int perf_unknown_cmd_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "help.autocorrect"))
|
||||
autocorrect = perf_config_int(var,value);
|
||||
/* Also use aliases for command lookup */
|
||||
if (!prefixcmp(var, "alias."))
|
||||
add_cmdname(&aliases, var + 6, strlen(var + 6));
|
||||
|
||||
return perf_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
static int levenshtein_compare(const void *p1, const void *p2)
|
||||
{
|
||||
const struct cmdname *const *c1 = p1, *const *c2 = p2;
|
||||
const char *s1 = (*c1)->name, *s2 = (*c2)->name;
|
||||
int l1 = (*c1)->len;
|
||||
int l2 = (*c2)->len;
|
||||
return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
|
||||
}
|
||||
|
||||
static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
|
||||
{
|
||||
int i;
|
||||
ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
|
||||
|
||||
for (i = 0; i < old->cnt; i++)
|
||||
cmds->names[cmds->cnt++] = old->names[i];
|
||||
free(old->names);
|
||||
old->cnt = 0;
|
||||
old->names = NULL;
|
||||
}
|
||||
|
||||
const char *help_unknown_cmd(const char *cmd)
|
||||
{
|
||||
int i, n, best_similarity = 0;
|
||||
struct cmdnames main_cmds, other_cmds;
|
||||
|
||||
memset(&main_cmds, 0, sizeof(main_cmds));
|
||||
memset(&other_cmds, 0, sizeof(main_cmds));
|
||||
memset(&aliases, 0, sizeof(aliases));
|
||||
|
||||
perf_config(perf_unknown_cmd_config, NULL);
|
||||
|
||||
load_command_list("perf-", &main_cmds, &other_cmds);
|
||||
|
||||
add_cmd_list(&main_cmds, &aliases);
|
||||
add_cmd_list(&main_cmds, &other_cmds);
|
||||
qsort(main_cmds.names, main_cmds.cnt,
|
||||
sizeof(main_cmds.names), cmdname_compare);
|
||||
uniq(&main_cmds);
|
||||
|
||||
/* This reuses cmdname->len for similarity index */
|
||||
for (i = 0; i < main_cmds.cnt; ++i)
|
||||
main_cmds.names[i]->len =
|
||||
levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
|
||||
|
||||
qsort(main_cmds.names, main_cmds.cnt,
|
||||
sizeof(*main_cmds.names), levenshtein_compare);
|
||||
|
||||
if (!main_cmds.cnt)
|
||||
die ("Uh oh. Your system reports no Git commands at all.");
|
||||
|
||||
best_similarity = main_cmds.names[0]->len;
|
||||
n = 1;
|
||||
while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
|
||||
++n;
|
||||
if (autocorrect && n == 1) {
|
||||
const char *assumed = main_cmds.names[0]->name;
|
||||
main_cmds.names[0] = NULL;
|
||||
clean_cmdnames(&main_cmds);
|
||||
fprintf(stderr, "WARNING: You called a Git program named '%s', "
|
||||
"which does not exist.\n"
|
||||
"Continuing under the assumption that you meant '%s'\n",
|
||||
cmd, assumed);
|
||||
if (autocorrect > 0) {
|
||||
fprintf(stderr, "in %0.1f seconds automatically...\n",
|
||||
(float)autocorrect/10.0);
|
||||
poll(NULL, 0, autocorrect * 100);
|
||||
}
|
||||
return assumed;
|
||||
}
|
||||
|
||||
fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
|
||||
|
||||
if (best_similarity < 6) {
|
||||
fprintf(stderr, "\nDid you mean %s?\n",
|
||||
n < 2 ? "this": "one of these");
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int cmd_version(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
printf("perf version %s\n", perf_version_string);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef HELP_H
|
||||
#define HELP_H
|
||||
|
||||
struct cmdnames {
|
||||
int alloc;
|
||||
int cnt;
|
||||
struct cmdname {
|
||||
size_t len; /* also used for similarity index in help.c */
|
||||
char name[FLEX_ARRAY];
|
||||
} **names;
|
||||
};
|
||||
|
||||
static inline void mput_char(char c, unsigned int num)
|
||||
{
|
||||
while(num--)
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
void load_command_list(const char *prefix,
|
||||
struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds);
|
||||
void add_cmdname(struct cmdnames *cmds, const char *name, int len);
|
||||
/* Here we require that excludes is a sorted list. */
|
||||
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
|
||||
int is_in_cmdlist(struct cmdnames *c, const char *s);
|
||||
void list_commands(const char *title, struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds);
|
||||
|
||||
#endif /* HELP_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user