+void
+message_log_maybe_newline ()
+{
+ if (message_log_need_newline)
+ message_dolog ("", 0, 1);
+}
+
+
+/* Add a string to the message log, optionally terminated with a newline.
+ This function calls low-level routines in order to bypass text property
+ hooks, etc. which might not be safe to run. */
+
+void
+message_dolog (m, len, nlflag)
+ char *m;
+ int len, nlflag;
+{
+ if (!NILP (Vmessage_log_max))
+ {
+ struct buffer *oldbuf;
+ int oldpoint, oldbegv, oldzv;
+ int old_windows_or_buffers_changed = windows_or_buffers_changed;
+
+ oldbuf = current_buffer;
+ Fset_buffer (Fget_buffer_create (build_string ("*Messages*")));
+ current_buffer->undo_list = Qt;
+ oldpoint = PT;
+ oldbegv = BEGV;
+ oldzv = ZV;
+ BEGV = BEG;
+ ZV = Z;
+ if (oldpoint == Z)
+ oldpoint += len + nlflag;
+ if (oldzv == Z)
+ oldzv += len + nlflag;
+ TEMP_SET_PT (Z);
+ if (len)
+ insert_1 (m, len, 1, 0);
+ if (nlflag)
+ {
+ int this_bol, prev_bol, dup;
+ insert_1 ("\n", 1, 1, 0);
+
+ this_bol = scan_buffer ('\n', Z, 0, -2, 0, 0);
+ if (this_bol > BEG)
+ {
+ prev_bol = scan_buffer ('\n', this_bol, 0, -2, 0, 0);
+ dup = message_log_check_duplicate (prev_bol, this_bol);
+ if (dup)
+ {
+ if (oldpoint > prev_bol)
+ oldpoint -= min (this_bol, oldpoint) - prev_bol;
+ if (oldbegv > prev_bol)
+ oldbegv -= min (this_bol, oldbegv) - prev_bol;
+ if (oldzv > prev_bol)
+ oldzv -= min (this_bol, oldzv) - prev_bol;
+ del_range_1 (prev_bol, this_bol, 0);
+ if (dup > 1)
+ {
+ char dupstr[40];
+ int duplen;
+
+ /* If you change this format, don't forget to also
+ change message_log_check_duplicate. */
+ sprintf (dupstr, " [%d times]", dup);
+ duplen = strlen (dupstr);
+ TEMP_SET_PT (Z-1);
+ if (oldpoint == Z)
+ oldpoint += duplen;
+ if (oldzv == Z)
+ oldzv += duplen;
+ insert_1 (dupstr, duplen, 1, 0);
+ }
+ }
+ }
+
+ if (NATNUMP (Vmessage_log_max))
+ {
+ int pos = scan_buffer ('\n', Z, 0,
+ -XFASTINT (Vmessage_log_max) - 1, 0, 0);
+ oldpoint -= min (pos, oldpoint) - BEG;
+ oldbegv -= min (pos, oldbegv) - BEG;
+ oldzv -= min (pos, oldzv) - BEG;
+ del_range_1 (BEG, pos, 0);
+ }
+ }
+ BEGV = oldbegv;
+ ZV = oldzv;
+ TEMP_SET_PT (oldpoint);
+ set_buffer_internal (oldbuf);
+ windows_or_buffers_changed = old_windows_or_buffers_changed;
+ message_log_need_newline = !nlflag;
+ }
+}
+
+
+/* We are at the end of the buffer after just having inserted a newline.
+ (Note: We depend on the fact we won't be crossing the gap.)
+ Check to see if the most recent message looks a lot like the previous one.
+ Return 0 if different, 1 if the new one should just replace it, or a
+ value N > 1 if we should also append " [N times]". */
+
+static int
+message_log_check_duplicate (prev_bol, this_bol)
+ int prev_bol, this_bol;
+{
+ int i;
+ int len = Z - 1 - this_bol;
+ int seen_dots = 0;
+ unsigned char *p1 = BUF_CHAR_ADDRESS (current_buffer, prev_bol);
+ unsigned char *p2 = BUF_CHAR_ADDRESS (current_buffer, this_bol);
+
+ for (i = 0; i < len; i++)
+ {
+ if (i >= 3 && p1[i-3] == '.' && p1[i-2] == '.' && p1[i-1] == '.'
+ && p1[i] != '\n')
+ seen_dots = 1;
+ if (p1[i] != p2[i])
+ return seen_dots;
+ }
+ p1 += len;
+ if (*p1 == '\n')
+ return 2;
+ if (*p1++ == ' ' && *p1++ == '[')
+ {
+ int n = 0;
+ while (*p1 >= '0' && *p1 <= '9')
+ n = n * 10 + *p1++ - '0';
+ if (strncmp (p1, " times]\n", 8) == 0)
+ return n+1;
+ }
+ return 0;
+}
+