diff --git a/Makefile b/Makefile index 272d335..b5b0dec 100644 --- a/Makefile +++ b/Makefile @@ -17,5 +17,6 @@ install: nuke if [[ ! $$prefix ]]; then \ prefix="/usr/local"; \ fi; \ - mkdir -pv $$prefix/bin; \ - cp -v nuke $$prefix/bin/ + mkdir -pv $$prefix/bin $$prefix/share/man/man1 \ + && cp -v nuke $$prefix/bin/ \ + && cp -v doc/nuke.1 $$prefix/share/man/man1/ diff --git a/doc/nuke.1 b/doc/nuke.1 new file mode 100644 index 0000000..dc85a24 --- /dev/null +++ b/doc/nuke.1 @@ -0,0 +1,37 @@ +.\" Manpage for nuke. +.\" Contact bruce@bruce-hill.com to correct errors or typos. +.TH man 8 "3 January 2019" "1.0" "nuke man page" +.SH NAME +nuke \- nuke files from orbit +.SH SYNOPSIS +.B nuke +[ +.I files... +] +.SH DESCRIPTION +Sometimes, deleting a file isn't enough. \fBnuke\fR will nuke them from orbit. +.SH INPUT +.TP +\fBnuke\fR will recursively delete all files and directories you provide it. +It will ask for confirmation before deleting. +.SH OPTIONS +.B \--help +Print the command line usage. +.SH EXAMPLES +.TP +.B +nuke node-v10.14.0/ +Nukes the directory node-v10.14.0/ + +.TP +.B +nuke tame_porn/ risque_porn/ outlandish_porn/ +Nukes all three directories + +.TP +.B +sudo nuke / +Uh, you might not want to try that one. + +.SH AUTHOR +Bruce Hill (bruce@bruce-hill.com) diff --git a/globe.h b/globe.h index fd4bddb..b2d3d2a 100644 --- a/globe.h +++ b/globe.h @@ -50,14 +50,14 @@ static double mix(double a, double b, double amount) return (1.-amount)*a + amount*b; } -void draw_stars() +void draw_stars(double t) { int rows,cols; getmaxyx(stdscr,rows,cols); attron(COLOR_PAIR(WHITE)); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { - double n = hillnoise(r*100,c*200); + double n = hillnoise(r*1,c*2+(t*.04)); if (n > .8) { mvaddch(r,c,n > .9 ? '*' : '.'); } @@ -66,11 +66,16 @@ void draw_stars() attroff(COLOR_PAIR(WHITE)); } +static double get_rotation(double t) +{ + return t * (2*M_PI)/60. + M_PI; +} + void draw_globe(double t, double zoom) { int rows,cols; getmaxyx(stdscr,rows,cols); - double rotation = t * (2*M_PI)/10. + M_PI; + double rotation = get_rotation(t); const double rho = rows/2.; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { @@ -114,15 +119,16 @@ void draw_globe(double t, double zoom) } } -void draw_scorch(double t, double zoom, int *targetr, int *targetc) +// return 1 if visible, else 0 +int get_target_pos(double t, double zoom, int *targetr, int *targetc) { int rows,cols; getmaxyx(stdscr,rows,cols); - double rotation = t * (2*M_PI)/10. + M_PI*1.4; + double rotation = get_rotation(t); const double rho = rows/2.; - double theta = M_PI/2*1.1; - double phi = M_PI + rotation; + double theta = M_PI/2*1.00; + double phi = M_PI + rotation + 2.3; double x = rho*sin(theta)*cos(phi); double y = rho*sin(theta)*sin(phi); double z = rho*cos(theta); @@ -130,58 +136,5 @@ void draw_scorch(double t, double zoom, int *targetr, int *targetc) int c = y / (.5/zoom) + cols/2; *targetr = r; *targetc = c; - - attron(COLOR_PAIR(BLACK) | A_BOLD); - mvprintw(r-1,c-1,"****"); - mvprintw(r,c-2, "******"); - mvprintw(r+1,c-1,"****"); - attroff(COLOR_PAIR(BLACK) | A_BOLD); -} - -void draw_target(double t, double zoom, int *targetr, int *targetc) -{ - int rows,cols; - getmaxyx(stdscr,rows,cols); - double rotation = t * (2*M_PI)/10. + M_PI*1.4; - const double rho = rows/2.; - - double theta = M_PI/2*1.1; - double phi = M_PI + rotation; - double x = rho*sin(theta)*cos(phi); - double y = rho*sin(theta)*sin(phi); - double z = rho*cos(theta); - int r = z / (1.0/zoom) + rows/2; - int c = y / (.5/zoom) + cols/2; - *targetr = r; - *targetc = c; - - if (x < 0) { - attron(COLOR_PAIR(YELLOW)); - mvaddch(r,c, '*' | A_BOLD); - attroff(COLOR_PAIR(YELLOW)); - } - - double wobble = t > 2.5 ? 0. : 2.5 - t; - if (wobble > .01) { - r = mix(r, rows*hillnoise(784,5.*t), wobble); - c = mix(c, cols*hillnoise(-784,5.*t), wobble); - } - - int attr = COLOR_PAIR(RED); - attron(attr); - mvaddch(r-1,c-2,ACS_ULCORNER | A_BOLD); - mvaddch(r-1,c-1,ACS_HLINE | A_BOLD); - //mvaddch(r-1,c-0,ACS_HLINE | A_BOLD); - mvaddch(r-1,c+1,ACS_HLINE | A_BOLD); - mvaddch(r-1,c+2,ACS_URCORNER | A_BOLD); - //mvaddch(r-0,c+2,ACS_VLINE | A_BOLD); - mvaddch(r+1,c+2,ACS_LRCORNER | A_BOLD); - mvaddch(r+1,c+1,ACS_HLINE | A_BOLD); - //mvaddch(r+1,c+0,ACS_HLINE | A_BOLD); - mvaddch(r+1,c-1,ACS_HLINE | A_BOLD); - mvaddch(r+1,c-2,ACS_LLCORNER | A_BOLD); - //mvaddch(r+0,c-2,ACS_VLINE | A_BOLD); - - mvaddch(r,c,'X' | A_BOLD); - attroff(attr); + return x < 0; } diff --git a/nuke.c b/nuke.c index 094aa7e..9fb48e3 100644 --- a/nuke.c +++ b/nuke.c @@ -15,7 +15,7 @@ #include #include -#define NUM_FRAMES 120 +#define NUM_FRAMES 100 #define NUM_BLOBS 800 #define PERSPECTIVE 50.0 #define ESCDELAY 10 @@ -120,6 +120,13 @@ int rmrf(char *path) int main(int argc, char *argv[]) { + if (argc > 1 && strcmp(argv[1], "--help") == 0) { + printf("nuke: Nuke files from orbit.\nUsage: nuke [files...]\n"); + return EXIT_SUCCESS; + } + + char *failure_message = "Did not delete."; + int rows,cols; int delay=1E5; spaceblob *blobs; @@ -178,79 +185,196 @@ int main(int argc, char *argv[]) curs_set(0); /* hide text cursor */ noecho(); + keypad(stdscr, 1); // First frame - erase(); int max_line = 0; for (int f = 0; f < num_files; f++) { int len = strlen(files[f]); if (len > max_line) max_line = len; } - for (int f = 0; f < num_files; f++) { - mvprintw(rows/2-num_files/2+f, cols/2-max_line/2, "%s", files[f]); + erase(); + int padwidth = max_line < cols*3/4 ? max_line : cols*3/4; + int padheight = num_files < rows*3/4 ? num_files : rows*3/4; + int padframer = rows/2-padheight/2-1; + int padframec = cols/2-padwidth/2-1; + WINDOW *padframe = newwin(padheight+2, padwidth+2, padframer, padframec); + if (!padframe) { + failure_message = "Couldn't create pad frame"; + goto exit_failure; } - refresh(); + wattron(padframe, COLOR_PAIR(YELLOW) | A_BOLD); + box(padframe, 0, 0); + WINDOW *pad = newpad(padheight, max_line); + if (!pad) { + failure_message = "Couldn't create pad"; + goto exit_failure; + } + /* Display confirmation */ attron(COLOR_PAIR(BLACK_ON_RED)); - const char confirm[] = " FIRE NUKE? y/n "; - mvprintw((rows*3)/4, cols/2 - strlen(confirm)/2, "%s", confirm); - attroff(COLOR_PAIR(BLACK_ON_RED)); + char description[] = " TARGET: "; + if (num_files > 1) { + description[13] = 'S', description[14] = ':'; + } + mvprintw(padframer-2, cols/2 - strlen(description)/2, "%s", description); + attron(COLOR_PAIR(BLACK_ON_RED) | A_BLINK); + const char confirm[] = " FIRE NUKE? y/n "; + mvprintw(padframer+padheight+3, cols/2 - strlen(confirm)/2, "%s", confirm); + attroff(COLOR_PAIR(BLACK_ON_RED) | A_BLINK); refresh(); + //immedok(pad, 1); + int scrollx = 0, scrolly = 0; + + timeout(10); + while (1) { + // Draw scroll bar + if (padheight < num_files) { + mvaddch(padframer, padframec+padwidth+2, ACS_UARROW); + mvaddch(padframer+padheight+1, padframec+padwidth+2, ACS_DARROW); + for (int i = 1; i <= padheight; i++) + mvaddch(padframer+i, padframec+padwidth+2, ACS_VLINE); + for (int i = scrolly*padheight/num_files; i <= scrolly*padheight/num_files+padheight*padheight/num_files; i++) + mvaddch(padframer+1+i, padframec+padwidth+2, ACS_CKBOARD); + } + box(padframe, 0, 0); + if (wrefresh(padframe) == ERR) goto exit_failure; + // Redo this each time so that pad doesn't have to hold everything in memory + werase(pad); + for (int f = scrolly; f < scrolly+padheight; f++) { + mvwprintw(pad,f-scrolly,0,"%s",files[f]); + } + if (prefresh(pad, 0, scrollx, padframer+1, padframec+1, padframer+padheight, padframec+padwidth) == ERR) + goto exit_failure; + //prefresh(pad, scrolly, scrollx, 1, 1, 1+padheight, 1+padwidth); + //prefresh(pad, scrolly, scrollx, rows/2-padheight/2, cols/2-padwidth/2, rows/2+padheight/2, cols/2+padwidth/2); + refresh(); + get_input: + switch(getch()) { + case 'y': + goto do_the_nuking; + case 'n': case 'q': case 27: + goto exit_failure; + case KEY_DOWN: case 'j': case ' ': + scrolly++; + break; + case KEY_UP: case 'k': + scrolly--; + break; + case KEY_RIGHT: case 'l': + scrollx++; + break; + case KEY_LEFT: case 'h': + scrollx--; + break; + case 'J': case KEY_NPAGE: + scrolly += num_files/20+1; + break; + case 'K': case KEY_PPAGE: + scrolly -= num_files/20+1; + break; + case KEY_SRIGHT: case 'L': + scrollx += 10; + break; + case KEY_SLEFT: case 'H': + scrollx -= 10; + break; + case KEY_HOME: case 'g': + scrolly = 0; + break; + case KEY_END: case 'G': + scrolly = num_files-padheight; + break; + default: + goto get_input; + } + if (scrolly < 0) scrolly = 0; + if (scrolly > num_files-padheight) scrolly = num_files-padheight; + if (scrollx < 0) scrollx = 0; + if (scrollx > max_line-padwidth) scrollx = max_line-padwidth; + } + + do_the_nuking: ; double zoom = .8; // percent of viewport globe will fill - /* - timeout(10); - chtype line[cols+1]; - for (int f = 0; 1; f = (f + 1) % num_files) { - int i; - for (i = 0; files[f][i]; i++) { - line[i] = files[f][i]; - } - for (; i < max_line; i++) line[i] = ' '; - line[i] = '\0'; - //mvprintw(rows/2, cols/2-max_line/2, "%s", files[f]); - mvaddchstr(rows/2, cols/2-max_line/2,line); - refresh(); - int ch = getch(); - if (ch == -1) - usleep(33333); // 30 FPS - else if (ch != 'y') - goto exit_failure; - else break; - } - */ - - if (getch() != 'y') { - goto exit_failure; - } - timeout(10); - + const double targeting_time = 2.5; + const double anticipation = 0.5; + const double firing_time = 1.0; + const double nuking_time = .5; + const double aftermath = 1.0; int f = 0; int targetr, targetc; - for (int i=0; i<30*3; i++) { + double t = 0.0; + for (; t < targeting_time + anticipation + firing_time; t += 1./30.) { erase(); - draw_stars(); - draw_globe((double)i/30., zoom); - draw_target((double)i/30, zoom, &targetr, &targetc); - attron(COLOR_PAIR(RED)); - mvaddch(targetr-1,targetc+1,'/'); - attroff(COLOR_PAIR(RED)); - attron(COLOR_PAIR(BLACK_ON_RED)); - mvprintw(targetr-2,targetc+2,"%s", argv[f+1]); - attroff(COLOR_PAIR(BLACK_ON_RED)); - f = (f + 1) % (argc - 1); + draw_stars(t); + draw_globe(t, zoom); + if (get_target_pos(t, zoom, &targetr, &targetc)) { + // If visible draw '*' on globe + attron(COLOR_PAIR(YELLOW)); + mvaddch(targetr,targetc, '*' | A_BOLD); + attroff(COLOR_PAIR(YELLOW)); + } + // Draw crosshair + double wobble = t > targeting_time ? 0. : targeting_time - t; + int r = targetr, c = targetc; + if (wobble > .01) { + r = mix(r, rows*hillnoise(784,5.*t), wobble); + c = mix(c, cols*hillnoise(-784,5.*t), wobble); + } + int attr = COLOR_PAIR(RED); + attron(attr); + mvaddch(r-1,c-2,ACS_ULCORNER | A_BOLD); + mvaddch(r-1,c-1,ACS_HLINE | A_BOLD); + mvaddch(r-1,c+1,ACS_HLINE | A_BOLD); + mvaddch(r-1,c+2,ACS_URCORNER | A_BOLD); + mvaddch(r+1,c+2,ACS_LRCORNER | A_BOLD); + mvaddch(r+1,c+1,ACS_HLINE | A_BOLD); + mvaddch(r+1,c-1,ACS_HLINE | A_BOLD); + mvaddch(r+1,c-2,ACS_LLCORNER | A_BOLD); + mvaddch(r,c,'X' | A_BOLD); + attroff(attr); + + // Draw label + if (get_target_pos(t, zoom, &targetr, &targetc)) { + attron(COLOR_PAIR(RED)); + mvaddch(targetr-1,targetc+1,'/'); + attroff(COLOR_PAIR(RED)); + attron(COLOR_PAIR(BLACK_ON_RED)); + mvprintw(targetr-2,targetc+2,"%s", argc > 1 ? argv[f+1] : "target"); + attroff(COLOR_PAIR(BLACK_ON_RED)); + if (argc > 2) + f = (f + 1) % (argc - 1); + } + + // Draw nuke + if (t > targeting_time + anticipation) { + double k = (t-targeting_time-anticipation)/firing_time; + int nuker = mix(rows, targetr, k), nukec = mix(cols/2, targetc, k); + attron(COLOR_PAIR(WHITE) | A_BOLD); + mvaddch(nuker, nukec, "=-. "[(int)(k*4.)]); + attroff(COLOR_PAIR(WHITE) | A_BOLD); + } + refresh(); + + // Check for input int ch = getch(); if (ch == 'q' || ch == 27) { - goto exit_success; + if (t <= targeting_time + anticipation) { + goto exit_failure; + } else { + goto exit_success; + } } usleep(33333); // 30 FPS } + t += nuking_time; for (int i=1; i "); + mvprintw(targetr-2,targetc+2," DELETED "); attroff(COLOR_PAIR(BLACK_ON_GREEN)); refresh(); int ch = getch(); @@ -297,5 +429,6 @@ exit_success: exit_failure: curs_set(1); /* unhide cursor */ endwin(); /* Exit ncurses */ + printf("%s\n",failure_message); return EXIT_FAILURE; }