Merge "Move file paging into ScreenRecoveryUI."
diff --git a/recovery.cpp b/recovery.cpp
index 1726a22..7776f1f 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -84,9 +84,6 @@
 
 #define KEEP_LOG_COUNT 10
 
-// Number of lines per page when displaying a file on screen
-#define LINES_PER_PAGE 30
-
 RecoveryUI* ui = NULL;
 char* locale = NULL;
 char recovery_version[PROPERTY_VALUE_MAX+1];
@@ -737,58 +734,6 @@
     }
 }
 
-static void file_to_ui(const char* fn) {
-    FILE *fp = fopen_path(fn, "re");
-    if (fp == NULL) {
-        ui->Print("  Unable to open %s: %s\n", fn, strerror(errno));
-        return;
-    }
-    char line[1024];
-    int ct = 0;
-    int key = 0;
-    redirect_stdio("/dev/null");
-    while(fgets(line, sizeof(line), fp) != NULL) {
-        ui->Print("%s", line);
-        ct++;
-        if (ct % LINES_PER_PAGE == 0) {
-            // give the user time to glance at the entries
-            key = ui->WaitKey();
-
-            if (key == KEY_POWER) {
-                break;
-            }
-
-            if (key == KEY_VOLUMEUP) {
-                // Go back by seeking to the beginning and dumping ct - n
-                // lines.  It's ugly, but this way we don't need to store
-                // the previous offsets.  The files we're dumping here aren't
-                // expected to be very large.
-                int i;
-
-                ct -= 2 * LINES_PER_PAGE;
-                if (ct < 0) {
-                    ct = 0;
-                }
-                fseek(fp, 0, SEEK_SET);
-                for (i = 0; i < ct; i++) {
-                    fgets(line, sizeof(line), fp);
-                }
-                ui->Print("^^^^^^^^^^\n");
-            }
-        }
-    }
-
-    // If the user didn't abort, then give the user time to glance at
-    // the end of the log, sorry, no rewind here
-    if (key != KEY_POWER) {
-        ui->Print("\n--END-- (press any key)\n");
-        ui->WaitKey();
-    }
-
-    redirect_stdio(TEMPORARY_LOG_FILE);
-    fclose(fp);
-}
-
 static void choose_recovery_file(Device* device) {
     unsigned int i;
     unsigned int n;
@@ -823,10 +768,14 @@
 
     const char* headers[] = { "Select file to view", "", NULL };
 
-    while(1) {
+    while (true) {
         int chosen_item = get_menu_selection(headers, entries, 1, 0, device);
         if (chosen_item == 0) break;
-        file_to_ui(entries[chosen_item]);
+
+        // TODO: do we need to redirect? ShowFile could just avoid writing to stdio.
+        redirect_stdio("/dev/null");
+        ui->ShowFile(entries[chosen_item]);
+        redirect_stdio(TEMPORARY_LOG_FILE);
     }
 
     for (i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) {
diff --git a/screen_ui.cpp b/screen_ui.cpp
index a0df455..6d8df68 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -86,8 +86,7 @@
 
 // Clear the screen and draw the currently selected background icon (if any).
 // Should only be called with updateMutex locked.
-void ScreenRecoveryUI::draw_background_locked(Icon icon)
-{
+void ScreenRecoveryUI::draw_background_locked(Icon icon) {
     pagesIdentical = false;
     gr_color(0, 0, 0, 255);
     gr_clear();
@@ -132,8 +131,7 @@
 
 // Draw the progress bar (if any) on the screen.  Does not flip pages.
 // Should only be called with updateMutex locked.
-void ScreenRecoveryUI::draw_progress_locked()
-{
+void ScreenRecoveryUI::draw_progress_locked() {
     if (currentIcon == ERROR) return;
 
     if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
@@ -204,8 +202,7 @@
 
 // Redraw everything on the screen.  Does not flip pages.
 // Should only be called with updateMutex locked.
-void ScreenRecoveryUI::draw_screen_locked()
-{
+void ScreenRecoveryUI::draw_screen_locked() {
     if (!show_text) {
         draw_background_locked(currentIcon);
         draw_progress_locked();
@@ -260,16 +257,14 @@
 
 // Redraw everything on the screen and flip the screen (make it visible).
 // Should only be called with updateMutex locked.
-void ScreenRecoveryUI::update_screen_locked()
-{
+void ScreenRecoveryUI::update_screen_locked() {
     draw_screen_locked();
     gr_flip();
 }
 
 // Updates only the progress bar, if possible, otherwise redraws the screen.
 // Should only be called with updateMutex locked.
-void ScreenRecoveryUI::update_progress_locked()
-{
+void ScreenRecoveryUI::update_progress_locked() {
     if (show_text || !pagesIdentical) {
         draw_screen_locked();    // Must redraw the whole screen
         pagesIdentical = true;
@@ -354,8 +349,7 @@
     return result;
 }
 
-void ScreenRecoveryUI::Init()
-{
+void ScreenRecoveryUI::Init() {
     gr_init();
 
     gr_font_size(&char_width, &char_height);
@@ -416,8 +410,7 @@
     }
 }
 
-void ScreenRecoveryUI::SetBackground(Icon icon)
-{
+void ScreenRecoveryUI::SetBackground(Icon icon) {
     pthread_mutex_lock(&updateMutex);
 
     currentIcon = icon;
@@ -426,8 +419,7 @@
     pthread_mutex_unlock(&updateMutex);
 }
 
-void ScreenRecoveryUI::SetProgressType(ProgressType type)
-{
+void ScreenRecoveryUI::SetProgressType(ProgressType type) {
     pthread_mutex_lock(&updateMutex);
     if (progressBarType != type) {
         progressBarType = type;
@@ -439,8 +431,7 @@
     pthread_mutex_unlock(&updateMutex);
 }
 
-void ScreenRecoveryUI::ShowProgress(float portion, float seconds)
-{
+void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
     pthread_mutex_lock(&updateMutex);
     progressBarType = DETERMINATE;
     progressScopeStart += progressScopeSize;
@@ -452,8 +443,7 @@
     pthread_mutex_unlock(&updateMutex);
 }
 
-void ScreenRecoveryUI::SetProgress(float fraction)
-{
+void ScreenRecoveryUI::SetProgress(float fraction) {
     pthread_mutex_lock(&updateMutex);
     if (fraction < 0.0) fraction = 0.0;
     if (fraction > 1.0) fraction = 1.0;
@@ -476,8 +466,7 @@
     pthread_mutex_unlock(&updateMutex);
 }
 
-void ScreenRecoveryUI::Print(const char *fmt, ...)
-{
+void ScreenRecoveryUI::Print(const char *fmt, ...) {
     char buf[256];
     va_list ap;
     va_start(ap, fmt);
@@ -486,10 +475,9 @@
 
     fputs(buf, stdout);
 
-    // This can get called before ui_init(), so be careful.
     pthread_mutex_lock(&updateMutex);
     if (text_rows > 0 && text_cols > 0) {
-        for (char* ptr = buf; *ptr != '\0'; ++ptr) {
+        for (const char* ptr = buf; *ptr != '\0'; ++ptr) {
             if (*ptr == '\n' || text_col >= text_cols) {
                 text[text_row][text_col] = '\0';
                 text_col = 0;
@@ -504,6 +492,75 @@
     pthread_mutex_unlock(&updateMutex);
 }
 
+// TODO: replace this with something not line-based so we can wrap correctly without getting
+// confused about what line we're on.
+void ScreenRecoveryUI::print_no_update(const char* s) {
+    pthread_mutex_lock(&updateMutex);
+    if (text_rows > 0 && text_cols > 0) {
+        for (const char* ptr = s; *ptr != '\0'; ++ptr) {
+            if (*ptr == '\n' || text_col >= text_cols) {
+                text[text_row][text_col] = '\0';
+                text_col = 0;
+                text_row = (text_row + 1) % text_rows;
+                if (text_row == text_top) text_top = (text_top + 1) % text_rows;
+            }
+            if (*ptr != '\n') text[text_row][text_col++] = *ptr;
+        }
+        text[text_row][text_col] = '\0';
+    }
+    pthread_mutex_unlock(&updateMutex);
+}
+
+void ScreenRecoveryUI::ShowFile(const char* filename) {
+    FILE* fp = fopen_path(filename, "re");
+    if (fp == nullptr) {
+        Print("  Unable to open %s: %s\n", filename, strerror(errno));
+        return;
+    }
+
+    char line[1024];
+    int ct = 0;
+    int key = 0;
+    while (fgets(line, sizeof(line), fp) != nullptr) {
+        print_no_update(line);
+        ct++;
+        if (ct % text_rows == 0) {
+            Redraw();
+
+            // give the user time to glance at the entries
+            key = WaitKey();
+
+            if (key == KEY_POWER) {
+                break;
+            } else if (key == KEY_VOLUMEUP) {
+                // Go back by seeking to the beginning and dumping ct - n
+                // lines.  It's ugly, but this way we don't need to store
+                // the previous offsets.  The files we're dumping here aren't
+                // expected to be very large.
+                ct -= 2 * text_rows;
+                if (ct < 0) {
+                    ct = 0;
+                }
+                fseek(fp, 0, SEEK_SET);
+                for (int i = 0; i < ct; i++) {
+                    fgets(line, sizeof(line), fp);
+                }
+                Print("^^^^^^^^^^\n");
+            } else {
+                // Next page.
+            }
+        }
+    }
+
+    // If the user didn't abort, then give the user time to glance at
+    // the end of the log, sorry, no rewind here
+    if (key != KEY_POWER) {
+        Print("\n--END-- (press any key)\n");
+        WaitKey();
+    }
+    fclose(fp);
+}
+
 void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
                                  int initial_selection) {
     pthread_mutex_lock(&updateMutex);
@@ -554,33 +611,29 @@
     pthread_mutex_unlock(&updateMutex);
 }
 
-bool ScreenRecoveryUI::IsTextVisible()
-{
+bool ScreenRecoveryUI::IsTextVisible() {
     pthread_mutex_lock(&updateMutex);
     int visible = show_text;
     pthread_mutex_unlock(&updateMutex);
     return visible;
 }
 
-bool ScreenRecoveryUI::WasTextEverVisible()
-{
+bool ScreenRecoveryUI::WasTextEverVisible() {
     pthread_mutex_lock(&updateMutex);
     int ever_visible = show_text_ever;
     pthread_mutex_unlock(&updateMutex);
     return ever_visible;
 }
 
-void ScreenRecoveryUI::ShowText(bool visible)
-{
+void ScreenRecoveryUI::ShowText(bool visible) {
     pthread_mutex_lock(&updateMutex);
     show_text = visible;
-    if (show_text) show_text_ever = 1;
+    if (show_text) show_text_ever = true;
     update_screen_locked();
     pthread_mutex_unlock(&updateMutex);
 }
 
-void ScreenRecoveryUI::Redraw()
-{
+void ScreenRecoveryUI::Redraw() {
     pthread_mutex_lock(&updateMutex);
     update_screen_locked();
     pthread_mutex_unlock(&updateMutex);
diff --git a/screen_ui.h b/screen_ui.h
index 82647ac..41ff4af 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -48,6 +48,7 @@
 
     // printing messages
     void Print(const char* fmt, ...) __printflike(2, 3);
+    void ShowFile(const char* filename);
 
     // menu display
     void StartMenu(const char* const * headers, const char* const * items,
@@ -80,11 +81,10 @@
     float progressScopeStart, progressScopeSize, progress;
     double progressScopeTime, progressScopeDuration;
 
-    // true when both graphics pages are the same (except for the
-    // progress bar)
+    // true when both graphics pages are the same (except for the progress bar).
     bool pagesIdentical;
 
-    // Log text overlay, displayed when a magic key is pressed
+    // Log text overlay, displayed when a magic key is pressed.
     char** text;
     size_t text_cols, text_rows;
     size_t text_col, text_row, text_top;
@@ -112,6 +112,8 @@
     static void* progress_thread(void* cookie);
     void progress_loop();
 
+    void print_no_update(const char*);
+
     void LoadBitmap(const char* filename, gr_surface* surface);
     void LoadBitmapArray(const char* filename, int* frames, gr_surface** surface);
     void LoadLocalizedBitmap(const char* filename, gr_surface* surface);
diff --git a/ui.h b/ui.h
index a0580b7..3b21774 100644
--- a/ui.h
+++ b/ui.h
@@ -31,10 +31,10 @@
     // Initialize the object; called before anything else.
     virtual void Init();
     // Show a stage indicator.  Call immediately after Init().
-    virtual void SetStage(int current, int max) { }
+    virtual void SetStage(int current, int max) = 0;
 
     // After calling Init(), you can tell the UI what locale it is operating in.
-    virtual void SetLocale(const char* locale) { }
+    virtual void SetLocale(const char* locale) = 0;
 
     // Set the overall recovery state ("background image").
     enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
@@ -65,6 +65,8 @@
     // toggled on the text display).
     virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0;
 
+    virtual void ShowFile(const char* filename) = 0;
+
     // --- key handling ---
 
     // Wait for keypress and return it.  May return -1 after timeout.
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 93a071e..82546ed 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -124,6 +124,8 @@
 // nothing but print.
 class FakeUI : public RecoveryUI {
     void Init() { }
+    void SetStage(int, int) { }
+    void SetLocale(const char*) { }
     void SetBackground(Icon icon) { }
 
     void SetProgressType(ProgressType determinate) { }
@@ -139,6 +141,7 @@
         vfprintf(stderr, fmt, ap);
         va_end(ap);
     }
+    void ShowFile(const char*) { }
 
     void StartMenu(const char* const * headers, const char* const * items,
                            int initial_selection) { }