tizen: kdbus: make i explicitly positive in make_single_header_vector
[platform/upstream/glib.git] / gio / gfilenamecompleter.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * SPDX-License-Identifier: LGPL-2.1-or-later
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include "config.h"
24 #include "gfilenamecompleter.h"
25 #include "gfileenumerator.h"
26 #include "gfileattribute.h"
27 #include "gfile.h"
28 #include "gfileinfo.h"
29 #include "gcancellable.h"
30 #include <string.h>
31 #include "glibintl.h"
32
33
34 /**
35  * GFilenameCompleter:
36  * 
37  * Completes partial file and directory names given a partial string by
38  * looking in the file system for clues. Can return a list of possible 
39  * completion strings for widget implementations.
40  */
41
42 enum {
43   GOT_COMPLETION_DATA,
44   LAST_SIGNAL
45 };
46
47 static guint signals[LAST_SIGNAL] = { 0 };
48
49 typedef struct {
50   GFilenameCompleter *completer;
51   GFileEnumerator *enumerator;
52   GCancellable *cancellable;
53   gboolean should_escape;
54   GFile *dir;
55   GList *basenames;
56   gboolean dirs_only;
57 } LoadBasenamesData;
58
59 struct _GFilenameCompleter {
60   GObject parent;
61
62   GFile *basenames_dir;
63   gboolean basenames_are_escaped;
64   gboolean dirs_only;
65   GList *basenames;
66
67   LoadBasenamesData *basename_loader;
68 };
69
70 G_DEFINE_TYPE (GFilenameCompleter, g_filename_completer, G_TYPE_OBJECT)
71
72 static void cancel_load_basenames (GFilenameCompleter *completer);
73
74 static void
75 g_filename_completer_finalize (GObject *object)
76 {
77   GFilenameCompleter *completer;
78
79   completer = G_FILENAME_COMPLETER (object);
80
81   cancel_load_basenames (completer);
82
83   if (completer->basenames_dir)
84     g_object_unref (completer->basenames_dir);
85
86   g_list_free_full (completer->basenames, g_free);
87
88   G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize (object);
89 }
90
91 static void
92 g_filename_completer_class_init (GFilenameCompleterClass *klass)
93 {
94   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
95   
96   gobject_class->finalize = g_filename_completer_finalize;
97   /**
98    * GFilenameCompleter::got-completion-data:
99    * 
100    * Emitted when the file name completion information comes available.
101    **/
102   signals[GOT_COMPLETION_DATA] = g_signal_new (I_("got-completion-data"),
103                                           G_TYPE_FILENAME_COMPLETER,
104                                           G_SIGNAL_RUN_LAST,
105                                           G_STRUCT_OFFSET (GFilenameCompleterClass, got_completion_data),
106                                           NULL, NULL,
107                                           NULL,
108                                           G_TYPE_NONE, 0);
109 }
110
111 static void
112 g_filename_completer_init (GFilenameCompleter *completer)
113 {
114 }
115
116 /**
117  * g_filename_completer_new:
118  * 
119  * Creates a new filename completer.
120  * 
121  * Returns: a #GFilenameCompleter.
122  **/
123 GFilenameCompleter *
124 g_filename_completer_new (void)
125 {
126   return g_object_new (G_TYPE_FILENAME_COMPLETER, NULL);
127 }
128
129 static char *
130 longest_common_prefix (char *a, char *b)
131 {
132   char *start;
133
134   start = a;
135
136   while (g_utf8_get_char (a) == g_utf8_get_char (b))
137     {
138       a = g_utf8_next_char (a);
139       b = g_utf8_next_char (b);
140     }
141
142   return g_strndup (start, a - start);
143 }
144
145 static void
146 load_basenames_data_free (LoadBasenamesData *data)
147 {
148   if (data->enumerator)
149     g_object_unref (data->enumerator);
150   
151   g_object_unref (data->cancellable);
152   g_object_unref (data->dir);
153   
154   g_list_free_full (data->basenames, g_free);
155   
156   g_free (data);
157 }
158
159 static void
160 got_more_files (GObject *source_object,
161                 GAsyncResult *res,
162                 gpointer user_data)
163 {
164   LoadBasenamesData *data = user_data;
165   GList *infos, *l;
166   GFileInfo *info;
167   const char *name;
168   gboolean append_slash;
169   char *t;
170   char *basename;
171
172   if (data->completer == NULL)
173     {
174       /* Was cancelled */
175       load_basenames_data_free (data);
176       return;
177     }
178
179   infos = g_file_enumerator_next_files_finish (data->enumerator, res, NULL);
180
181   for (l = infos; l != NULL; l = l->next)
182     {
183       info = l->data;
184
185       if (data->dirs_only &&
186           g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
187         {
188           g_object_unref (info);
189           continue;
190         }
191       
192       append_slash = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
193       name = g_file_info_get_name (info);
194       if (name == NULL)
195         {
196           g_object_unref (info);
197           continue;
198         }
199
200       
201       if (data->should_escape)
202         basename = g_uri_escape_string (name,
203                                         G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
204                                         TRUE);
205       else
206         /* If not should_escape, must be a local filename, convert to utf8 */
207         basename = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
208       
209       if (basename)
210         {
211           if (append_slash)
212             {
213               t = basename;
214               basename = g_strconcat (basename, "/", NULL);
215               g_free (t);
216             }
217           
218           data->basenames = g_list_prepend (data->basenames, basename);
219         }
220       
221       g_object_unref (info);
222     }
223   
224   g_list_free (infos);
225   
226   if (infos)
227     {
228       /* Not last, get more files */
229       g_file_enumerator_next_files_async (data->enumerator,
230                                           100,
231                                           0,
232                                           data->cancellable,
233                                           got_more_files, data);
234     }
235   else
236     {
237       data->completer->basename_loader = NULL;
238       
239       if (data->completer->basenames_dir)
240         g_object_unref (data->completer->basenames_dir);
241       g_list_free_full (data->completer->basenames, g_free);
242       
243       data->completer->basenames_dir = g_object_ref (data->dir);
244       data->completer->basenames = data->basenames;
245       data->completer->basenames_are_escaped = data->should_escape;
246       data->basenames = NULL;
247       
248       g_file_enumerator_close_async (data->enumerator, 0, NULL, NULL, NULL);
249
250       g_signal_emit (data->completer, signals[GOT_COMPLETION_DATA], 0);
251       load_basenames_data_free (data);
252     }
253 }
254
255
256 static void
257 got_enum (GObject *source_object,
258           GAsyncResult *res,
259           gpointer user_data)
260 {
261   LoadBasenamesData *data = user_data;
262
263   if (data->completer == NULL)
264     {
265       /* Was cancelled */
266       load_basenames_data_free (data);
267       return;
268     }
269   
270   data->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
271   
272   if (data->enumerator == NULL)
273     {
274       data->completer->basename_loader = NULL;
275
276       if (data->completer->basenames_dir)
277         g_object_unref (data->completer->basenames_dir);
278       g_list_free_full (data->completer->basenames, g_free);
279
280       /* Mark up-to-date with no basenames */
281       data->completer->basenames_dir = g_object_ref (data->dir);
282       data->completer->basenames = NULL;
283       data->completer->basenames_are_escaped = data->should_escape;
284       
285       load_basenames_data_free (data);
286       return;
287     }
288   
289   g_file_enumerator_next_files_async (data->enumerator,
290                                       100,
291                                       0,
292                                       data->cancellable,
293                                       got_more_files, data);
294 }
295
296 static void
297 schedule_load_basenames (GFilenameCompleter *completer,
298                          GFile *dir,
299                          gboolean should_escape)
300 {
301   LoadBasenamesData *data;
302
303   cancel_load_basenames (completer);
304
305   data = g_new0 (LoadBasenamesData, 1);
306   data->completer = completer;
307   data->cancellable = g_cancellable_new ();
308   data->dir = g_object_ref (dir);
309   data->should_escape = should_escape;
310   data->dirs_only = completer->dirs_only;
311
312   completer->basename_loader = data;
313   
314   g_file_enumerate_children_async (dir,
315                                    G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
316                                    0, 0,
317                                    data->cancellable,
318                                    got_enum, data);
319 }
320
321 static void
322 cancel_load_basenames (GFilenameCompleter *completer)
323 {
324   LoadBasenamesData *loader;
325   
326   if (completer->basename_loader)
327     {
328       loader = completer->basename_loader; 
329       loader->completer = NULL;
330       
331       g_cancellable_cancel (loader->cancellable);
332       
333       completer->basename_loader = NULL;
334     }
335 }
336
337
338 /* Returns a list of possible matches and the basename to use for it */
339 static GList *
340 init_completion (GFilenameCompleter *completer,
341                  const char *initial_text,
342                  char **basename_out)
343 {
344   gboolean should_escape;
345   GFile *file, *parent;
346   char *basename;
347   char *t;
348   int len;
349
350   *basename_out = NULL;
351   
352   should_escape = ! (g_path_is_absolute (initial_text) || *initial_text == '~');
353
354   len = strlen (initial_text);
355   
356   if (len > 0 &&
357       initial_text[len - 1] == '/')
358     return NULL;
359   
360   file = g_file_parse_name (initial_text);
361   parent = g_file_get_parent (file);
362   if (parent == NULL)
363     {
364       g_object_unref (file);
365       return NULL;
366     }
367
368   if (completer->basenames_dir == NULL ||
369       completer->basenames_are_escaped != should_escape ||
370       !g_file_equal (parent, completer->basenames_dir))
371     {
372       schedule_load_basenames (completer, parent, should_escape);
373       g_object_unref (file);
374       return NULL;
375     }
376   
377   basename = g_file_get_basename (file);
378   if (should_escape)
379     {
380       t = basename;
381       basename = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
382       g_free (t);
383     }
384   else
385     {
386       t = basename;
387       basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
388       g_free (t);
389       
390       if (basename == NULL)
391         return NULL;
392     }
393
394   *basename_out = basename;
395
396   return completer->basenames;
397 }
398
399 /**
400  * g_filename_completer_get_completion_suffix:
401  * @completer: the filename completer.
402  * @initial_text: text to be completed.
403  *
404  * Obtains a completion for @initial_text from @completer.
405  *  
406  * Returns: (nullable) (transfer full): a completed string, or %NULL if no
407  *     completion exists. This string is not owned by GIO, so remember to g_free()
408  *     it when finished.
409  **/
410 char *
411 g_filename_completer_get_completion_suffix (GFilenameCompleter *completer,
412                                             const char *initial_text)
413 {
414   GList *possible_matches, *l;
415   char *prefix;
416   char *suffix;
417   char *possible_match;
418   char *lcp;
419
420   g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
421   g_return_val_if_fail (initial_text != NULL, NULL);
422
423   possible_matches = init_completion (completer, initial_text, &prefix);
424
425   suffix = NULL;
426   
427   for (l = possible_matches; l != NULL; l = l->next)
428     {
429       possible_match = l->data;
430       
431       if (g_str_has_prefix (possible_match, prefix))
432         {
433           if (suffix == NULL)
434             suffix = g_strdup (possible_match + strlen (prefix));
435           else
436             {
437               lcp = longest_common_prefix (suffix,
438                                            possible_match + strlen (prefix));
439               g_free (suffix);
440               suffix = lcp;
441               
442               if (*suffix == 0)
443                 break;
444             }
445         }
446     }
447
448   g_free (prefix);
449   
450   return suffix;
451 }
452
453 /**
454  * g_filename_completer_get_completions:
455  * @completer: the filename completer.
456  * @initial_text: text to be completed.
457  * 
458  * Gets an array of completion strings for a given initial text.
459  * 
460  * Returns: (array zero-terminated=1) (transfer full): array of strings with possible completions for @initial_text.
461  * This array must be freed by g_strfreev() when finished. 
462  **/
463 char **
464 g_filename_completer_get_completions (GFilenameCompleter *completer,
465                                       const char         *initial_text)
466 {
467   GList *possible_matches, *l;
468   char *prefix;
469   char *possible_match;
470   GPtrArray *res;
471
472   g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
473   g_return_val_if_fail (initial_text != NULL, NULL);
474
475   possible_matches = init_completion (completer, initial_text, &prefix);
476
477   res = g_ptr_array_new ();
478   for (l = possible_matches; l != NULL; l = l->next)
479     {
480       possible_match = l->data;
481
482       if (g_str_has_prefix (possible_match, prefix))
483         g_ptr_array_add (res,
484                          g_strconcat (initial_text, possible_match + strlen (prefix), NULL));
485     }
486
487   g_free (prefix);
488
489   g_ptr_array_add (res, NULL);
490
491   return (char**)g_ptr_array_free (res, FALSE);
492 }
493
494 /**
495  * g_filename_completer_set_dirs_only:
496  * @completer: the filename completer.
497  * @dirs_only: a #gboolean.
498  * 
499  * If @dirs_only is %TRUE, @completer will only 
500  * complete directory names, and not file names.
501  **/
502 void
503 g_filename_completer_set_dirs_only (GFilenameCompleter *completer,
504                                     gboolean dirs_only)
505 {
506   g_return_if_fail (G_IS_FILENAME_COMPLETER (completer));
507
508   completer->dirs_only = dirs_only;
509 }