From f0b4716cdc3763e5adfaf6ba16bb8261007c8e02 Mon Sep 17 00:00:00 2001
From: jp7g21 <jp7g21@soton.ac.uk>
Date: Sun, 14 Nov 2021 11:58:38 +0000
Subject: [PATCH] Final tweaks

---
 drake/test1/main     | Bin 0 -> 24408 bytes
 drake/test2/Makefile |   3 +
 duck/duck.c          |   9 +-
 gduckb/Makefile      |   2 +
 gduckb/gdbinit       |   2 +
 gduckb/gduckb.c      | 231 +++++++++++++++++++++++++++++++++++++++++++
 libduck/libduck.c    |  17 +++-
 libduck/libduck.h    |   2 +
 8 files changed, 255 insertions(+), 11 deletions(-)
 create mode 100755 drake/test1/main
 create mode 100644 drake/test2/Makefile
 create mode 100644 gduckb/Makefile
 create mode 100644 gduckb/gdbinit
 create mode 100644 gduckb/gduckb.c

diff --git a/drake/test1/main b/drake/test1/main
new file mode 100755
index 0000000000000000000000000000000000000000..f184c09488f33bb06aa276cba7bcc7e9ca63bc0a
GIT binary patch
literal 24408
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

literal 0
HcmV?d00001

diff --git a/drake/test2/Makefile b/drake/test2/Makefile
new file mode 100644
index 0000000..9152907
--- /dev/null
+++ b/drake/test2/Makefile
@@ -0,0 +1,3 @@
+
+all:
+	ls /doesntexist
diff --git a/duck/duck.c b/duck/duck.c
index 280eb9e..3f52e8f 100644
--- a/duck/duck.c
+++ b/duck/duck.c
@@ -82,15 +82,12 @@ int main(int argc, char **argv)
 	  } else if (strcmp(argv[i], "nod") == 0) {
 	       if (argv[i+1]) {
 		    int numnods = atoi(argv[i+1]);
-		    shake(MOTOR_2, numnods, -90, 0, 250);
+		    shake(MOTOR_2, numnods, 0, 90, 250);
 		    i++;
 	       }
 	  } else if (strcmp(argv[i], "facepalm") == 0) {
-	       if (argv[i+1]) {
-		    int numnods = atoi(argv[i+1]);
-		    shake(MOTOR_2, numnods, -90, 0, 250);
-		    i++;
-	       }
+	       duck_set_velocity(2, 30, 3000);
+	       duck_delay(3000);
 	  } else if (strcmp(argv[i], "print") == 0) {
 	       if (argv[i+1]) {
 		    duck_write_text(argv[i+1]);
diff --git a/gduckb/Makefile b/gduckb/Makefile
new file mode 100644
index 0000000..7862772
--- /dev/null
+++ b/gduckb/Makefile
@@ -0,0 +1,2 @@
+gduckb: gduckb.c ../libduck/libduck.so
+	cc gduckb.c -o gduckb -L ../libduck -l duck -g
diff --git a/gduckb/gdbinit b/gduckb/gdbinit
new file mode 100644
index 0000000..1ca4a9c
--- /dev/null
+++ b/gduckb/gdbinit
@@ -0,0 +1,2 @@
+b main
+r a.out
diff --git a/gduckb/gduckb.c b/gduckb/gduckb.c
new file mode 100644
index 0000000..8efabd4
--- /dev/null
+++ b/gduckb/gduckb.c
@@ -0,0 +1,231 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <err.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "../libduck/libduck.h"
+
+int this_in_gdb_out[2];
+int gdb_in_this_out[2];
+
+void setup_pipes(void)
+{
+     if (pipe(gdb_in_this_out) ||
+       pipe(this_in_gdb_out)) err(EXIT_FAILURE, "Pipe failed");
+     /*this_in_gdb_out[0] = STDIN_FILENO;
+       gdb_in_this_out[1] = STDOUT_FILENO;*/
+}
+
+void launch_gdb(const char *prog)
+{
+     int res = fork();
+     if (res < 0) {
+	  err(EXIT_FAILURE, "fork failed");
+     } else if (res == 0) { /* Child */
+	  dup2(gdb_in_this_out[0], STDIN_FILENO);
+	  dup2(this_in_gdb_out[1], STDOUT_FILENO);
+	  
+	  if (execl("/bin/gdb", "/bin/gdb", "--interpreter=mi3", "-x", "gdbinit", prog, (char *)NULL)) {
+	       err(EXIT_FAILURE, "Failed to launch gdb");
+	  }
+     } else { /* Parent */
+	  return;
+     }
+}
+
+enum {
+     STATE_LINENO, STATE_WHITESPACE, STATE_CODE, STATE_NL
+};
+
+void process_code_line(const char *str)
+{
+     if (str[0] != '~' || str[1] != '"') return;
+
+     int state = STATE_LINENO;
+     static char lineno_buf[20];
+     int lb_ind = 0;
+     int lineno;
+     static char code_buf[200];
+     int code_ind = 0;
+     int esc = 0;
+     
+     for (const char *c=str+2; *c; c++) {
+	  switch (state) {
+	  case STATE_LINENO:
+	       if (isdigit(*c)) {
+		    lineno_buf[lb_ind++] = *c;
+	       } else if (c[0] == '\\' && c[1] == 't') {
+		    lineno_buf[lb_ind] = 0;
+		    lineno = atoi(lineno_buf);
+		    if (lineno <= 0) return;
+		    state = STATE_WHITESPACE;
+		    c += 2;
+	       }
+	       break;
+	  case STATE_WHITESPACE:
+	       if (!isspace(*c)) {
+		    state = STATE_CODE;
+		    code_buf[code_ind++] = *c;
+	       }
+	       break;
+	  case STATE_CODE:
+	       if (esc) {
+		    if (*c == 't') code_buf[code_ind++] = '\t';
+		    if (*c == '\\') code_buf[code_ind++] = '\\';
+		    if (*c == '"') code_buf[code_ind++] = '"';
+		    if (*c == '\'') code_buf[code_ind++] = '\'';
+		    if (*c == 'n') state = STATE_NL;
+		    esc = 0;
+	       } else if (*c == '\\') {
+		    esc = 1;
+	       } else {
+		    code_buf[code_ind++] = *c;
+	       }
+	       break;
+	  case STATE_NL:
+	       code_buf[code_ind] = 0;
+	       duck_write_text(code_buf);
+	       break;
+	  }
+     }
+}
+
+/* Line of code is last ~ line before prompt, matching line pattern */
+
+enum { STATE_PROMPT, STATE_TILDE_LINE, STATE_OTHER_LINE };
+
+void parse_line(const char *line)
+{
+     static int state = STATE_OTHER_LINE;
+     static char codelinebuf[200];
+
+     switch (state) {
+     case STATE_PROMPT:
+	  if (line[0] == '~') {
+	       state = STATE_TILDE_LINE;
+	       strncpy(codelinebuf, line, 199);
+	  }
+	  break;
+     case STATE_TILDE_LINE:
+	  if (line[0] != '~') {
+	       state = STATE_OTHER_LINE;
+	       process_code_line(codelinebuf);
+	  }
+	  break;
+     case STATE_OTHER_LINE:
+	  if (strcmp(line, "(gdb)") == 0) {
+	       state = STATE_PROMPT;
+	  }
+	  if (line[0] == '~') process_code_line(line);
+	  break;
+     }
+}
+	       
+
+char buf[4096];
+int buf_ind = 0;
+
+char linebuf[200];
+int linebuf_ind = 0;
+
+void read_data(void)
+{
+     int n;
+
+     if ((n=read(this_in_gdb_out[0], buf, 4065)) <= 0) {
+	  return;
+     } else {
+	  for (int i=0; i < n; i++) {
+	       if (buf[i] == '\n') {
+		    linebuf[linebuf_ind] = '\0';
+		    parse_line(linebuf);
+		    linebuf_ind = 0;
+	       } else {
+		    linebuf[linebuf_ind++] = buf[i];
+	       }
+	  }
+     }
+}
+
+void sleep_ms(int ms)
+{
+     struct timespec ts = {
+	  .tv_sec=ms/1000, .tv_nsec=(ms % 1000) * 1000000
+     };
+     nanosleep(&ts, NULL);
+}
+
+void shake(int motor, int number, int min, int max, int delay)
+{
+     int delay1 = (delay * (-min)) / (max - min);
+     int delay2 = delay - delay1;
+     
+     duck_set_velocity(motor, 100, 100);
+     duck_delay(100);
+     duck_set_velocity(motor, -100, 200);
+     duck_delay(200);
+     duck_set_velocity(motor, 100, 100);
+     duck_delay(100);
+}
+int interact_gdb(void)
+{
+     FILE *to_gdb = fdopen(gdb_in_this_out[1], "w");
+     if (!to_gdb) err(EXIT_FAILURE, "fdopen failed");
+     setbuf(to_gdb, NULL);
+     
+     struct pollfd fds[] = {
+	  { .fd = this_in_gdb_out[0], POLLIN, 0 },
+	  { .fd = duckfd, POLLIN, 0 },
+     };
+
+     static char tmp[4096];
+
+     /*read(this_in_gdb_out[0], tmp, 4096);
+     fprintf(to_gdb, "b main\n");
+     sleep_ms(300);
+     read(this_in_gdb_out[0], tmp, 4096);
+     fprintf(to_gdb, "r\n");*/
+     sleep_ms(300);
+
+     while (1) {
+	  if (poll(fds, 2, -1)) {
+	       if (fds[0].revents) {
+		    read_data();
+		    fds[0].revents = 0;
+	       }
+	       if (fds[1].revents) {
+		    int c = duck_getc();
+		    fprintf(to_gdb, c == 'a' ? "s\n" : "n\n");
+		    shake(MOTOR_1, 2, -20, 20, 200);
+		    fds[1].revents = 0;
+	       }
+	  }
+     }
+}
+
+
+
+int main(int argc, char **argv)
+{
+     duck_debug_mode = 0;
+     if (argc < 2) {
+	  printf("Usage: gduckb program-name\n");
+	  return -1;
+     }
+     
+     setup_pipes();
+     launch_gdb("a.out");
+     open_duck(DEFAULT_DUCK_FNAME);
+     configure_duck();
+
+     if (interact_gdb()) {
+	  err(EXIT_FAILURE, "gdb interaction failed");
+     }
+     close_duck();
+     return 0;
+     
+}
diff --git a/libduck/libduck.c b/libduck/libduck.c
index d0e210f..f967f3f 100644
--- a/libduck/libduck.c
+++ b/libduck/libduck.c
@@ -11,14 +11,14 @@
 # define DUCK_BAUD B115200
 #endif /* DUCK_BAUD */
 
-static int duckfd = 0;
+int duckfd = 0;
 
 int duck_debug_mode = 0;
 
 void open_duck(const char *fname)
 {
      if (duck_debug_mode) {
-	  printf("Opening duck\n");
+	  fprintf(stderr, "Opening duck\n");
      } else {
 	  duckfd = open(fname, O_NOCTTY | O_RDWR);
 	  
@@ -31,7 +31,7 @@ void open_duck(const char *fname)
 void configure_duck(void)
 {
      if (duck_debug_mode) {
-	  printf("Configuring duck\n");
+	  fprintf(stderr, "Configuring duck\n");
      } else {
 	  struct termios tio;
 	  if (tcgetattr(duckfd, &tio)) {
@@ -51,7 +51,7 @@ static int duck_printf(const char *fmt, ...)
      va_list ap;
      va_start(ap, fmt);
      if (duck_debug_mode) {
-	  return vprintf(fmt, ap);
+	  return vfprintf(stderr, fmt, ap);
      } else {
 	  return vdprintf(duckfd, fmt, ap) < 0;
      }
@@ -103,10 +103,17 @@ void read_duck_to_stdout(void)
      }
 }
 
+int duck_getc(void)
+{
+     char c;
+     if (read(duckfd, &c, 1)) return c;
+     else return EOF;
+}
+
 void close_duck(void)
 {
      if (duck_debug_mode) {
-	  printf("Closing duck\n");
+	  fprintf(stderr, "Closing duck\n");
      } else {
 	  close(duckfd);
      }
diff --git a/libduck/libduck.h b/libduck/libduck.h
index 9ce0166..524c3de 100644
--- a/libduck/libduck.h
+++ b/libduck/libduck.h
@@ -10,6 +10,7 @@
 #endif /* DEFAULT_DUCK_FNAME */
 
 extern int duck_debug_mode;
+extern int duckfd;
 
 void open_duck(const char *fname);
 void configure_duck(void);
@@ -18,6 +19,7 @@ int duck_delay(int ms);
 int duck_set_velocity(int motor, int deg_per_sec, int ms);
 int duck_write_text(const char *str);
 void read_duck_to_stdout(void);
+int duck_getc(void);
 void close_duck(void);
 
 #endif /* _LIBDUCK_H */
-- 
GitLab