@@ -16,6 +16,11 @@ |
| 16 | 16 | #include "error.h" |
| 17 | 17 | #include "utils.h" |
| 18 | 18 | #include "git_ops.h" |
| 19 | +#include "ssh_manager.h" |
| 20 | + |
| 21 | +/* Long-only options (no short form). Values above 0xff avoid colliding with |
| 22 | + * ASCII short options handled by getopt_long. */ |
| 23 | +#define OPT_SSH_AGENT_INFO 0x100 |
| 19 | 24 | |
| 20 | 25 | static void print_usage(const char *prog_name) { |
| 21 | 26 | printf("Usage: %s [OPTIONS] [COMMAND] [ARGS]\n", prog_name); |
@@ -28,6 +33,7 @@ static void print_usage(const char *prog_name) { |
| 28 | 33 | printf(" status Show current account status\n"); |
| 29 | 34 | printf(" doctor, health Run comprehensive health check\n"); |
| 30 | 35 | printf(" config Show configuration file information\n"); |
| 36 | + printf(" init <shell> Emit shell integration (fish|bash|zsh|sh)\n"); |
| 31 | 37 | printf(" <account> Switch to specified account\n"); |
| 32 | 38 | printf("\nOptions:\n"); |
| 33 | 39 | printf(" --global, -g Use global git scope\n"); |
@@ -67,6 +73,8 @@ static int handle_status_command(gitswitch_ctx_t *ctx); |
| 67 | 73 | static int handle_switch_command(gitswitch_ctx_t *ctx, const char *identifier); |
| 68 | 74 | static int handle_doctor_command(gitswitch_ctx_t *ctx); |
| 69 | 75 | static int handle_config_command(gitswitch_ctx_t *ctx); |
| 76 | +static int handle_init_command(const char *shell); |
| 77 | +static const char *detect_shell_from_env(void); |
| 70 | 78 | |
| 71 | 79 | int main(int argc, char *argv[]) { |
| 72 | 80 | gitswitch_ctx_t ctx; |
@@ -88,6 +96,9 @@ int main(int argc, char *argv[]) { |
| 88 | 96 | {"dry-run", no_argument, 0, 'n'}, |
| 89 | 97 | {"global", no_argument, 0, 'g'}, |
| 90 | 98 | {"local", no_argument, 0, 'l'}, |
| 99 | + /* Compat alias for the Python gitswitch era. Dispatches to `init` |
| 100 | + * with shell auto-detected from $SHELL so stale rc lines keep working. */ |
| 101 | + {"ssh-agent-info", no_argument, 0, OPT_SSH_AGENT_INFO}, |
| 91 | 102 | {0, 0, 0, 0} |
| 92 | 103 | }; |
| 93 | 104 | |
@@ -129,6 +140,11 @@ int main(int argc, char *argv[]) { |
| 129 | 140 | case 'l': |
| 130 | 141 | /* Local scope - will be handled by command handlers */ |
| 131 | 142 | break; |
| 143 | + case OPT_SSH_AGENT_INFO: { |
| 144 | + int rc = handle_init_command(detect_shell_from_env()); |
| 145 | + error_cleanup(); |
| 146 | + return rc; |
| 147 | + } |
| 132 | 148 | default: |
| 133 | 149 | print_usage(argv[0]); |
| 134 | 150 | error_cleanup(); |
@@ -211,6 +227,8 @@ int main(int argc, char *argv[]) { |
| 211 | 227 | exit_code = handle_doctor_command(&ctx); |
| 212 | 228 | } else if (strcmp(command, "config") == 0) { |
| 213 | 229 | exit_code = handle_config_command(&ctx); |
| 230 | + } else if (strcmp(command, "init") == 0) { |
| 231 | + exit_code = handle_init_command(arg1 ? arg1 : detect_shell_from_env()); |
| 214 | 232 | } else { |
| 215 | 233 | /* Assume it's an account identifier for switching */ |
| 216 | 234 | exit_code = handle_switch_command(&ctx, command); |
@@ -224,12 +242,13 @@ int main(int argc, char *argv[]) { |
| 224 | 242 | strcmp(command, "rm") == 0 || |
| 225 | 243 | strcmp(command, "delete") == 0) { |
| 226 | 244 | should_save = true; |
| 227 | | - } else if (strcmp(command, "list") != 0 && |
| 245 | + } else if (strcmp(command, "list") != 0 && |
| 228 | 246 | strcmp(command, "ls") != 0 && |
| 229 | 247 | strcmp(command, "status") != 0 && |
| 230 | 248 | strcmp(command, "doctor") != 0 && |
| 231 | 249 | strcmp(command, "health") != 0 && |
| 232 | | - strcmp(command, "config") != 0) { |
| 250 | + strcmp(command, "config") != 0 && |
| 251 | + strcmp(command, "init") != 0) { |
| 233 | 252 | /* Assume it's a switch command - may have modified default scope */ |
| 234 | 253 | should_save = true; |
| 235 | 254 | } |
@@ -387,7 +406,63 @@ static int handle_config_command(gitswitch_ctx_t *ctx) { |
| 387 | 406 | display_warning("Configuration file has unsafe permissions (%o)", file_mode & 0777); |
| 388 | 407 | } |
| 389 | 408 | } |
| 390 | | - |
| 409 | + |
| 391 | 410 | return EXIT_SUCCESS; |
| 392 | 411 | } |
| 393 | 412 | |
| 413 | +/* Return the basename of $SHELL, or NULL if it can't be determined. The |
| 414 | + * pointer aliases into the environment string — callers must not free it. */ |
| 415 | +static const char *detect_shell_from_env(void) { |
| 416 | + const char *shell = getenv("SHELL"); |
| 417 | + if (!shell || !*shell) { |
| 418 | + return NULL; |
| 419 | + } |
| 420 | + const char *slash = strrchr(shell, '/'); |
| 421 | + return slash ? slash + 1 : shell; |
| 422 | +} |
| 423 | + |
| 424 | +/* Emit shell-integration snippet for `shell` on stdout. The snippet sets |
| 425 | + * SSH_AUTH_SOCK to the stable gitswitch symlink, guarded by a socket test so |
| 426 | + * sourcing before the first switch (or after /tmp is wiped) is silent. */ |
| 427 | +static int handle_init_command(const char *shell) { |
| 428 | + char sock_path[MAX_PATH_LEN]; |
| 429 | + if (ssh_manager_get_auth_sock_path(sock_path, sizeof(sock_path)) != 0) { |
| 430 | + fprintf(stderr, "gitswitch: failed to compute SSH_AUTH_SOCK path: %s\n", |
| 431 | + get_last_error()->message); |
| 432 | + return EXIT_FAILURE; |
| 433 | + } |
| 434 | + |
| 435 | + if (!shell || !*shell) { |
| 436 | + fprintf(stderr, |
| 437 | + "gitswitch: could not detect shell; pass one explicitly:\n" |
| 438 | + " gitswitch init fish | source\n" |
| 439 | + " eval \"$(gitswitch init bash)\"\n" |
| 440 | + " eval \"$(gitswitch init zsh)\"\n"); |
| 441 | + return EXIT_FAILURE; |
| 442 | + } |
| 443 | + |
| 444 | + if (strcmp(shell, "fish") == 0) { |
| 445 | + printf("# gitswitch shell integration (fish)\n"); |
| 446 | + printf("set -l __gitswitch_auth_sock %s\n", sock_path); |
| 447 | + printf("if test -S $__gitswitch_auth_sock\n"); |
| 448 | + printf(" set -gx SSH_AUTH_SOCK $__gitswitch_auth_sock\n"); |
| 449 | + printf("end\n"); |
| 450 | + printf("set -e __gitswitch_auth_sock\n"); |
| 451 | + return EXIT_SUCCESS; |
| 452 | + } |
| 453 | + |
| 454 | + if (strcmp(shell, "bash") == 0 || strcmp(shell, "zsh") == 0 || |
| 455 | + strcmp(shell, "sh") == 0 || strcmp(shell, "dash") == 0 || |
| 456 | + strcmp(shell, "ksh") == 0) { |
| 457 | + printf("# gitswitch shell integration (%s)\n", shell); |
| 458 | + printf("__gitswitch_auth_sock=%s\n", sock_path); |
| 459 | + printf("[ -S \"$__gitswitch_auth_sock\" ] && export SSH_AUTH_SOCK=\"$__gitswitch_auth_sock\"\n"); |
| 460 | + printf("unset __gitswitch_auth_sock\n"); |
| 461 | + return EXIT_SUCCESS; |
| 462 | + } |
| 463 | + |
| 464 | + fprintf(stderr, |
| 465 | + "gitswitch: unsupported shell '%s' (supported: fish, bash, zsh, sh, dash, ksh)\n", |
| 466 | + shell); |
| 467 | + return EXIT_FAILURE; |
| 468 | +} |