+ location->start.column = 0;
+ location->end.column = 0;
+}
+
+void
+msg_location_merge (struct msg_location **dstp, const struct msg_location *src)
+{
+ struct msg_location *dst = *dstp;
+ if (!dst)
+ {
+ *dstp = msg_location_dup (src);
+ return;
+ }
+
+ if (dst->file_name != src->file_name)
+ {
+ /* Failure. */
+ return;
+ }
+ if (msg_point_compare_3way (&dst->start, &src->start) > 0)
+ dst->start = src->start;
+ if (msg_point_compare_3way (&dst->end, &src->end) < 0)
+ dst->end = src->end;
+}
+
+struct msg_location *
+msg_location_merged (const struct msg_location *a,
+ const struct msg_location *b)
+{
+ struct msg_location *new = msg_location_dup (a);
+ if (b)
+ msg_location_merge (&new, b);
+ return new;
+}
+
+struct msg_location *
+msg_location_dup (const struct msg_location *src)
+{
+ if (!src)
+ return NULL;
+
+ struct msg_location *dst = xmalloc (sizeof *dst);
+ *dst = *src;
+ if (src->file_name)
+ dst->file_name = intern_ref (src->file_name);
+ if (msg_handler.lex_source_ref && src->src)
+ msg_handler.lex_source_ref (dst->src);
+ return dst;
+}
+
+bool
+msg_location_is_empty (const struct msg_location *loc)
+{
+ return !loc || (!loc->file_name
+ && loc->start.line <= 0
+ && loc->start.column <= 0);
+}
+
+void
+msg_location_format (const struct msg_location *loc, struct string *s)
+{
+ if (!loc)
+ return;
+
+ if (loc->file_name)
+ ds_put_cstr (s, loc->file_name);
+
+ int l1 = loc->start.line;
+ int l2 = MAX (l1, loc->end.line);
+ int c1 = loc->start.column;
+ int c2 = MAX (c1, loc->end.column);
+
+ if (l1 > 0)
+ {
+ if (loc->file_name)
+ ds_put_byte (s, ':');
+
+ if (l2 > l1)
+ {
+ if (c1 > 0)
+ ds_put_format (s, "%d.%d-%d.%d", l1, c1, l2, c2);
+ else
+ ds_put_format (s, "%d-%d", l1, l2);
+ }
+ else
+ {
+ if (c1 > 0)
+ {
+ if (c2 > c1)
+ {
+ /* The GNU coding standards say to use
+ LINENO-1.COLUMN-1-COLUMN-2 for this case, but GNU
+ Emacs interprets COLUMN-2 as LINENO-2 if I do that.
+ I've submitted an Emacs bug report:
+ http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7725.
+
+ For now, let's be compatible. */
+ ds_put_format (s, "%d.%d-%d.%d", l1, c1, l1, c2);
+ }
+ else
+ ds_put_format (s, "%d.%d", l1, c1);
+ }
+ else
+ ds_put_format (s, "%d", l1);
+ }
+ }
+ else if (c1 > 0)
+ {
+ if (c2 > c1)
+ ds_put_format (s, ".%d-%d", c1, c2);
+ else
+ ds_put_format (s, ".%d", c1);
+ }
+}
+\f
+/* msg_stack */
+
+void
+msg_stack_destroy (struct msg_stack *stack)
+{
+ if (stack)
+ {
+ msg_location_destroy (stack->location);
+ free (stack->description);
+ free (stack);
+ }
+}
+
+struct msg_stack *
+msg_stack_dup (const struct msg_stack *src)
+{
+ struct msg_stack *dst = xmalloc (sizeof *src);
+ *dst = (struct msg_stack) {
+ .location = msg_location_dup (src->location),
+ .description = xstrdup_if_nonnull (src->description),
+ };
+ return dst;