gnulib: update
[bison.git] / src / fixits.c
blob3467cdaed74044ec413c8fa41621a92156f2c543
1 /* Support for fixing grammar files.
3 Copyright (C) 2019-2021 Free Software Foundation, Inc.
5 This file is part of Bison, the GNU Compiler Compiler.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <https://www.gnu.org/licenses/>. */
20 #include <config.h>
22 #include "fixits.h"
24 #include <error.h>
25 #include <get-errno.h>
26 #include <gl_array_list.h>
27 #include <gl_xlist.h>
28 #include <progname.h>
29 #include <quote.h>
30 #include <quotearg.h>
31 #include <vasnprintf.h>
33 #include "system.h"
35 #include "files.h"
36 #include "getargs.h"
38 typedef struct
40 location location;
41 char *fix;
42 } fixit;
44 gl_list_t fixits = NULL;
46 static fixit *
47 fixit_new (location const *loc, char const* fix)
49 fixit *res = xmalloc (sizeof *res);
50 res->location = *loc;
51 res->fix = xstrdup (fix);
52 return res;
55 static int
56 fixit_cmp (const fixit *a, const fixit *b)
58 return location_cmp (a->location, b->location);
61 static void
62 fixit_free (fixit *f)
64 free (f->fix);
65 free (f);
69 /* GCC and Clang follow the same pattern.
70 https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html
71 https://clang.llvm.org/docs/UsersManual.html#cmdoption-fdiagnostics-parseable-fixits */
72 static void
73 fixit_print (fixit const *f, FILE *out)
75 fprintf (out, "fix-it:%s:{%d:%d-%d:%d}:%s\n",
76 quotearg_n_style (1, c_quoting_style, f->location.start.file),
77 f->location.start.line, f->location.start.byte,
78 f->location.end.line, f->location.end.byte,
79 quotearg_n_style (2, c_quoting_style, f->fix));
83 void
84 fixits_register (location const *loc, char const* fix)
86 if (!fixits)
87 fixits = gl_list_create_empty (GL_ARRAY_LIST,
88 /* equals */ NULL,
89 /* hashcode */ NULL,
90 (gl_listelement_dispose_fn) fixit_free,
91 true);
92 fixit *f = fixit_new (loc, fix);
93 gl_sortedlist_add (fixits, (gl_listelement_compar_fn) fixit_cmp, f);
94 if (feature_flag & feature_fixit)
95 fixit_print (f, stderr);
99 bool
100 fixits_empty (void)
102 return !fixits;
106 void
107 fixits_run (void)
109 if (!fixits)
110 return;
112 /* This is not unlike what is done in location_caret. */
113 uniqstr input = ((fixit *) gl_list_get_at (fixits, 0))->location.start.file;
114 /* Backup the file. */
115 char buf[256];
116 size_t len = sizeof (buf);
117 char *backup = asnprintf (buf, &len, "%s~", input);
118 if (!backup)
119 xalloc_die ();
120 if (rename (input, backup))
121 error (EXIT_FAILURE, get_errno (),
122 _("%s: cannot backup"), quotearg_colon (input));
123 FILE *in = xfopen (backup, "r");
124 FILE *out = xfopen (input, "w");
125 size_t line = 1;
126 size_t offset = 1;
127 void const *p = NULL;
128 gl_list_iterator_t iter = gl_list_iterator (fixits);
129 while (gl_list_iterator_next (&iter, &p, NULL))
131 fixit const *f = p;
132 /* Look for the correct line. */
133 while (line < f->location.start.line)
135 int c = getc (in);
136 if (c == EOF)
137 break;
138 if (c == '\n')
140 ++line;
141 offset = 1;
143 putc (c, out);
146 /* Look for the right offset. */
147 bool need_eol = false;
148 while (offset < f->location.start.byte)
150 int c = getc (in);
151 if (c == EOF)
152 break;
153 ++offset;
154 if (c == '\n')
155 /* The position we are asked for is beyond the actual
156 line: pad with spaces, and remember we need a \n. */
157 need_eol = true;
158 putc (need_eol ? ' ' : c, out);
161 /* Paste the fix instead. */
162 fputs (f->fix, out);
164 /* Maybe install the eol afterwards. */
165 if (need_eol)
166 putc ('\n', out);
168 /* Skip the bad input. */
169 while (line < f->location.end.line)
171 int c = getc (in);
172 if (c == EOF)
173 break;
174 if (c == '\n')
176 ++line;
177 offset = 1;
180 while (offset < f->location.end.byte)
182 int c = getc (in);
183 if (c == EOF)
184 break;
185 ++offset;
188 /* If erasing the content of a full line, also remove the
189 end-of-line. */
190 if (f->fix[0] == 0 && f->location.start.byte == 1)
192 int c = getc (in);
193 if (c == EOF)
194 break;
195 else if (c == '\n')
197 ++line;
198 offset = 1;
200 else
201 ungetc (c, in);
204 /* Paste the rest of the file. */
206 int c;
207 while ((c = getc (in)) != EOF)
208 putc (c, out);
211 gl_list_iterator_free (&iter);
212 xfclose (out);
213 xfclose (in);
214 fprintf (stderr, "%s: file %s was updated (backup: %s)\n",
215 program_name, quote_n (0, input), quote_n (1, backup));
216 if (backup != buf)
217 free (backup);
221 /* Free the registered fixits. */
222 void fixits_free (void)
224 if (fixits)
226 gl_list_free (fixits);
227 fixits = NULL;