summaryrefslogtreecommitdiffstats
path: root/data/lineup-parameters.c
diff options
context:
space:
mode:
Diffstat (limited to 'data/lineup-parameters.c')
-rw-r--r--data/lineup-parameters.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/data/lineup-parameters.c b/data/lineup-parameters.c
new file mode 100644
index 0000000..51a01e1
--- /dev/null
+++ b/data/lineup-parameters.c
@@ -0,0 +1,483 @@
+/*
+ * This file is part of gnome-c-utils.
+ *
+ * Copyright © 2013 Sébastien Wilmet <swilmet@gnome.org>
+ *
+ * gnome-c-utils is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gnome-c-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gnome-c-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Line up parameters of function declarations.
+ *
+ * Usage: lineup-parameters [file]
+ * If the file is not given, stdin is read.
+ * The result is printed to stdout.
+ *
+ * The restrictions:
+ * - The function name must be at column 0, followed by a space and an opening
+ * parenthesis;
+ * - One parameter per line;
+ * - A paramater must follow certain rules (see the regex in the code), but it
+ * doesn't accept all possibilities of the C language.
+ * - The opening curly brace ("{") of the function must also be at column 0.
+ *
+ * If one restriction is missing, the function declaration is not modified.
+ *
+ * Example:
+ *
+ * gboolean
+ * frobnitz (Frobnitz *frobnitz,
+ * gint magic_number,
+ * GError **error)
+ * {
+ * ...
+ * }
+ *
+ * Becomes:
+ *
+ * gboolean
+ * frobnitz (Frobnitz *frobnitz,
+ * gint magic_number,
+ * GError **error)
+ * {
+ * ...
+ * }
+ */
+
+/*
+ * Use with Vim:
+ *
+ * Although this script can be used in Vim (or other text editors), a Vim plugin
+ * exists:
+ * http://damien.lespiau.name/blog/2009/12/07/aligning-c-function-parameters-with-vim/
+ *
+ * You can use a selection:
+ * - place the cursor at the function's name;
+ * - press V to start the line selection;
+ * - press ]] to go to the "{";
+ * - type ":" followed by "!lineup-parameters".
+ *
+ * Note: the "{" is required in the selection, to detect that we are in a
+ * function declaration.
+ *
+ * You can easily map these steps with a keybinding (F8 in the example below).
+ * Note that I'm not a Vim expert, so there is maybe a better way to configure
+ * this stuff.
+ *
+ * function! LineupParameters()
+ * let l:winview = winsaveview()
+ * execute "normal {V]]:!lineup-parameters\<CR>"
+ * call winrestview(l:winview)
+ * endfunction
+ *
+ * autocmd Filetype c map <F8> :call LineupParameters()<CR>
+ */
+
+/* TODO support "..." vararg parameter. */
+
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <unistd.h>
+
+#define USE_TABS FALSE
+
+typedef struct
+{
+ gchar *type;
+ guint nb_stars;
+ gchar *name;
+} ParameterInfo;
+
+static void
+parameter_info_free (ParameterInfo *param_info)
+{
+ g_free (param_info->type);
+ g_free (param_info->name);
+ g_slice_free (ParameterInfo, param_info);
+}
+
+static gboolean
+match_function_name (const gchar *line,
+ gchar **function_name,
+ gint *first_param_pos)
+{
+ static GRegex *regex = NULL;
+ GMatchInfo *match_info;
+ gint end_pos;
+ gboolean match = FALSE;
+
+ if (G_UNLIKELY (regex == NULL))
+ regex = g_regex_new ("^(\\w+) ?\\(", G_REGEX_OPTIMIZE, 0, NULL);
+
+ g_regex_match (regex, line, 0, &match_info);
+
+ if (g_match_info_matches (match_info) &&
+ g_match_info_fetch_pos (match_info, 1, NULL, &end_pos) &&
+ g_match_info_fetch_pos (match_info, 0, NULL, first_param_pos))
+ {
+ match = TRUE;
+
+ if (function_name != NULL)
+ *function_name = g_strndup (line, end_pos);
+ }
+
+ g_match_info_free (match_info);
+ return match;
+}
+
+static gboolean
+match_parameter (gchar *line,
+ ParameterInfo **info,
+ gboolean *is_last_parameter)
+{
+ static GRegex *regex = NULL;
+ GMatchInfo *match_info;
+ gint start_pos = 0;
+
+ if (G_UNLIKELY (regex == NULL))
+ regex = g_regex_new ("^\\s*(?<type>(const\\s+)?\\w+)\\s+(?<stars>\\**)\\s*(?<name>\\w+)\\s*(?<end>,|\\))\\s*$",
+ G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
+
+ if (is_last_parameter != NULL)
+ *is_last_parameter = FALSE;
+
+ match_function_name (line, NULL, &start_pos);
+
+ g_regex_match (regex, line + start_pos, 0, &match_info);
+
+ if (!g_match_info_matches (match_info))
+ {
+ g_match_info_free (match_info);
+ return FALSE;
+ }
+
+ if (info != NULL)
+ {
+ gchar *stars;
+
+ *info = g_slice_new0 (ParameterInfo);
+
+ (*info)->type = g_match_info_fetch_named (match_info, "type");
+ (*info)->name = g_match_info_fetch_named (match_info, "name");
+ g_assert ((*info)->type != NULL);
+ g_assert ((*info)->name != NULL);
+
+ stars = g_match_info_fetch_named (match_info, "stars");
+ (*info)->nb_stars = strlen (stars);
+ g_free (stars);
+ }
+
+ if (is_last_parameter != NULL)
+ {
+ gchar *end = g_match_info_fetch_named (match_info, "end");
+ *is_last_parameter = g_str_equal (end, ")");
+ g_free (end);
+ }
+
+ g_match_info_free (match_info);
+ return TRUE;
+}
+
+static gboolean
+match_opening_curly_brace (const gchar *line)
+{
+ static GRegex *regex = NULL;
+
+ if (G_UNLIKELY (regex == NULL))
+ regex = g_regex_new ("^{\\s*$", G_REGEX_OPTIMIZE, 0, NULL);
+
+ return g_regex_match (regex, line, 0, NULL);
+}
+
+/* Returns the number of lines that take the function declaration.
+ * Returns 0 if not a function declaration. */
+static guint
+get_function_declaration_length (gchar **lines)
+{
+ guint nb_lines = 1;
+ gchar **cur_line = lines;
+
+ while (*cur_line != NULL)
+ {
+ gboolean match_param;
+ gboolean is_last_param;
+
+ match_param = match_parameter (*cur_line, NULL, &is_last_param);
+
+ if (is_last_param)
+ {
+ gchar *next_line = *(cur_line + 1);
+
+ if (next_line == NULL ||
+ !match_opening_curly_brace (next_line))
+ return 0;
+
+ return nb_lines;
+ }
+
+ if (!match_param)
+ return 0;
+
+ nb_lines++;
+ cur_line++;
+ }
+ /* should not be reachable - but silences a compiler warning */
+ return 0;
+}
+
+static GSList *
+get_list_parameter_infos (gchar **lines,
+ guint length)
+{
+ GSList *list = NULL;
+ gint i;
+
+ for (i = length - 1; i >= 0; i--)
+ {
+ ParameterInfo *info = NULL;
+
+ match_parameter (lines[i], &info, NULL);
+ g_assert (info != NULL);
+
+ list = g_slist_prepend (list, info);
+ }
+
+ return list;
+}
+
+static void
+compute_spacing (GSList *parameter_infos,
+ guint *max_type_length,
+ guint *max_stars_length)
+{
+ GSList *l;
+ *max_type_length = 0;
+ *max_stars_length = 0;
+
+ for (l = parameter_infos; l != NULL; l = l->next)
+ {
+ ParameterInfo *info = l->data;
+ guint type_length = strlen (info->type);
+
+ if (type_length > *max_type_length)
+ *max_type_length = type_length;
+
+ if (info->nb_stars > *max_stars_length)
+ *max_stars_length = info->nb_stars;
+ }
+}
+
+static void
+print_parameter (ParameterInfo *info,
+ guint max_type_length,
+ guint max_stars_length)
+{
+ gint type_length;
+ gint nb_spaces;
+ gchar *spaces;
+ gchar *stars;
+
+ g_print ("%s", info->type);
+
+ type_length = strlen (info->type);
+ nb_spaces = max_type_length - type_length;
+ g_assert (nb_spaces >= 0);
+
+ spaces = g_strnfill (nb_spaces, ' ');
+ g_print ("%s ", spaces);
+ g_free (spaces);
+
+ nb_spaces = max_stars_length - info->nb_stars;
+ g_assert (nb_spaces >= 0);
+ spaces = g_strnfill (nb_spaces, ' ');
+ g_print ("%s", spaces);
+ g_free (spaces);
+
+ stars = g_strnfill (info->nb_stars, '*');
+ g_print ("%s", stars);
+ g_free (stars);
+
+ g_print ("%s", info->name);
+}
+
+static void
+print_function_declaration (gchar **lines,
+ guint length)
+{
+ gchar **cur_line = lines;
+ gchar *function_name;
+ gint nb_spaces_to_parenthesis;
+ GSList *parameter_infos;
+ GSList *l;
+ guint max_type_length;
+ guint max_stars_length;
+ gchar *spaces;
+
+ if (!match_function_name (*cur_line, &function_name, NULL))
+ g_error ("The line doesn't match a function name.");
+
+ g_print ("%s (", function_name);
+
+ nb_spaces_to_parenthesis = strlen (function_name) + 2;
+
+ if (USE_TABS)
+ {
+ gchar *tabs = g_strnfill (nb_spaces_to_parenthesis / 8, '\t');
+ gchar *spaces_after_tabs = g_strnfill (nb_spaces_to_parenthesis % 8, ' ');
+
+ spaces = g_strdup_printf ("%s%s", tabs, spaces_after_tabs);
+
+ g_free (tabs);
+ g_free (spaces_after_tabs);
+ }
+ else
+ {
+ spaces = g_strnfill (nb_spaces_to_parenthesis, ' ');
+ }
+
+ parameter_infos = get_list_parameter_infos (lines, length);
+ compute_spacing (parameter_infos, &max_type_length, &max_stars_length);
+
+ for (l = parameter_infos; l != NULL; l = l->next)
+ {
+ ParameterInfo *info = l->data;
+
+ if (l != parameter_infos)
+ g_print ("%s", spaces);
+
+ print_parameter (info, max_type_length, max_stars_length);
+
+ if (l->next != NULL)
+ g_print (",\n");
+ }
+
+ g_print (")\n");
+
+ g_free (function_name);
+ g_free (spaces);
+ g_slist_free_full (parameter_infos, (GDestroyNotify)parameter_info_free);
+}
+
+static void
+parse_contents (gchar **lines)
+{
+ gchar **cur_line = lines;
+
+ /* Skip the empty last line, to avoid adding an extra \n. */
+ for (cur_line = lines; cur_line[0] != NULL && cur_line[1] != NULL; cur_line++)
+ {
+ guint length;
+
+ if (!match_function_name (*cur_line, NULL, NULL))
+ {
+ g_print ("%s\n", *cur_line);
+ continue;
+ }
+
+ length = get_function_declaration_length (cur_line);
+
+ if (length == 0)
+ {
+ g_print ("%s\n", *cur_line);
+ continue;
+ }
+
+ print_function_declaration (cur_line, length);
+
+ cur_line += length - 1;
+ }
+}
+
+static gchar *
+get_file_contents (gchar *arg)
+{
+ GFile *file;
+ gchar *path;
+ gchar *contents;
+ GError *error = NULL;
+
+ file = g_file_new_for_commandline_arg (arg);
+ path = g_file_get_path (file);
+ g_file_get_contents (path, &contents, NULL, &error);
+
+ if (error != NULL)
+ g_error ("Impossible to get file contents: %s", error->message);
+
+ g_object_unref (file);
+ g_free (path);
+ return contents;
+}
+
+static gchar *
+get_stdin_contents (void)
+{
+ GInputStream *stream;
+ GString *string;
+ GError *error = NULL;
+
+ stream = g_unix_input_stream_new (STDIN_FILENO, FALSE);
+ string = g_string_new ("");
+
+ while (TRUE)
+ {
+ gchar buffer[4097] = { '\0' };
+ gssize nb_bytes_read = g_input_stream_read (stream, buffer, 4096, NULL, &error);
+
+ if (nb_bytes_read == 0)
+ break;
+
+ if (error != NULL)
+ g_error ("Impossible to read stdin: %s", error->message);
+
+ g_string_append (string, buffer);
+ }
+
+ g_input_stream_close (stream, NULL, NULL);
+ g_object_unref (stream);
+
+ return g_string_free (string, FALSE);
+}
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ gchar *contents;
+ gchar **contents_lines;
+
+ setlocale (LC_ALL, "");
+
+ if (argc > 2)
+ {
+ g_printerr ("Usage: %s [file]\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ if (argc == 2)
+ contents = get_file_contents (argv[1]);
+ else
+ contents = get_stdin_contents ();
+
+ contents_lines = g_strsplit (contents, "\n", 0);
+ g_free (contents);
+
+ parse_contents (contents_lines);
+
+ return EXIT_SUCCESS;
+}