// retoor #include "bash_repair.h" #include #include #include #include #include static char *ensure_shebang(const char *text) { if (!text || !*text) return strdup(""); // Check if it already has a shebang const char *p = text; while (*p && isspace((unsigned char)*p)) p++; if (strncmp(p, "#!", 2) == 0 || strncmp(p, ": <<", 4) == 0) { return strdup(text); } // Heuristic: if it has multiple lines, add shebang if (strchr(text, '\n')) { char *result = malloc(strlen(text) + 32); if (!result) return strdup(text); strcpy(result, "#!/usr/bin/env bash\n"); strcat(result, text); return result; } return strdup(text); } static char *normalize_whitespace_and_operators(const char *src) { if (!src) return NULL; size_t src_len = strlen(src); char *result = malloc(src_len * 2 + 1); if (!result) return NULL; char *dst = result; const char *curr = src; while (*curr) { if (*curr == '\r') { curr++; continue; } // Detect operators to normalize spaces around them const char *ops[] = {"||", "&&", ">>", "|&", "|", ";", ">", "<", NULL}; bool matched_op = false; for (int i = 0; ops[i]; i++) { size_t op_len = strlen(ops[i]); if (strncmp(curr, ops[i], op_len) == 0) { // Remove preceding spaces in dst if any while (dst > result && isspace((unsigned char)*(dst - 1)) && *(dst - 1) != '\n') dst--; if (dst > result && *(dst - 1) != '\n') *dst++ = ' '; memcpy(dst, ops[i], op_len); dst += op_len; curr += op_len; // Skip following spaces in curr while (*curr && isspace((unsigned char)*curr) && *curr != '\n') curr++; if (*curr && *curr != '\n') *dst++ = ' '; matched_op = true; break; } } if (!matched_op) { *dst++ = *curr++; } } *dst = '\0'; // Second pass to strip trailing spaces on each line char *s2 = strdup(result); free(result); if (!s2) return NULL; char *final = malloc(strlen(s2) + 1); char *f_ptr = final; char *line = s2; while (line && *line) { char *next_line = strchr(line, '\n'); size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); const char *end = line + line_len - 1; while (end >= line && isspace((unsigned char)*end)) end--; size_t new_len = (size_t)(end - line + 1); memcpy(f_ptr, line, new_len); f_ptr += new_len; if (next_line) { *f_ptr++ = '\n'; line = next_line + 1; } else { break; } } *f_ptr = '\0'; free(s2); return final; } static char *fix_line_issues(const char *src) { if (!src) return NULL; size_t src_len = strlen(src); char *result = malloc(src_len * 2 + 1024); if (!result) return NULL; char *dst = result; const char *line = src; while (line && *line) { const char *next_line = strchr(line, '\n'); size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); char line_buf[4096]; if (line_len >= sizeof(line_buf)) line_len = sizeof(line_buf) - 1; memcpy(line_buf, line, line_len); line_buf[line_len] = '\0'; // 1. Tiny Shell Lint (else if -> elif) char *else_if = strstr(line_buf, "else if "); if (else_if) { // Very basic replacement memmove(else_if + 4, else_if + 8, strlen(else_if + 8) + 1); memcpy(else_if, "elif", 4); } // 2. Fix unbalanced quotes int single = 0, double_q = 0; bool escaped = false; for (size_t i = 0; i < strlen(line_buf); i++) { if (escaped) { escaped = false; continue; } if (line_buf[i] == '\\') escaped = true; else if (line_buf[i] == '\'') single++; else if (line_buf[i] == '"') double_q++; } if (single % 2 == 1 && double_q == 0) strcat(line_buf, "'"); else if (double_q % 2 == 1 && single == 0) strcat(line_buf, "\""); // 3. Fix trailing operators size_t cur_len = strlen(line_buf); const char *ops[] = {"||", "&&", ">>", "|&", "|", ">", "<", NULL}; for (int i = 0; ops[i]; i++) { size_t op_len = strlen(ops[i]); if (cur_len >= op_len) { if (strcmp(line_buf + cur_len - op_len, ops[i]) == 0) { // Comment it char temp[4096]; strcpy(temp, line_buf); temp[cur_len - op_len] = '\0'; strcat(temp, "# "); strcat(temp, ops[i]); strcpy(line_buf, temp); break; } } } // 4. Dangerous rm -rf check if (strstr(line_buf, "sudo rm -rf /") || strstr(line_buf, "rm -rf / ")) { strcpy(dst, "# WARNING: potentially destructive command detected\n"); dst += strlen(dst); } strcpy(dst, line_buf); dst += strlen(dst); if (next_line) *dst++ = '\n'; if (next_line) line = next_line + 1; else break; } *dst = '\0'; return result; } static char *collapse_nested_bash_c(const char *src) { if (!src) return NULL; // Pattern: bash -c "bash -c '...'") // We'll just do a very basic string replacement for common variants char *s1 = strdup(src); char *patterns[] = { "bash -c \"bash -c '", "bash -c 'bash -c \"", NULL }; for (int i = 0; patterns[i]; i++) { char *p; while ((p = strstr(s1, patterns[i]))) { // Find closing quotes char outer = patterns[i][8]; // " or ' char inner = patterns[i][18]; // ' or " char *inner_end = strchr(p + 19, inner); if (inner_end && *(inner_end + 1) == outer) { // We can collapse. // Original: [p]bash -c "bash -c 'cmd'"[end] // New: [p]bash -c 'cmd'[end] size_t cmd_len = (size_t)(inner_end - (p + 19)); char *new_s = malloc(strlen(s1) + 1); size_t prefix_len = (size_t)(p - s1); memcpy(new_s, s1, prefix_len); char *d = new_s + prefix_len; strcpy(d, "bash -c "); d += 8; *d++ = inner; memcpy(d, p + 19, cmd_len); d += cmd_len; *d++ = inner; strcpy(d, inner_end + 2); free(s1); s1 = new_s; } else { break; } } } return s1; } char *bash_repair_command(const char *src) { if (!src) return NULL; char *s1 = normalize_whitespace_and_operators(src); char *s2 = fix_line_issues(s1); free(s1); char *s3 = collapse_nested_bash_c(s2); free(s2); char *s4 = ensure_shebang(s3); free(s3); return s4; }