From 5c8763be0e68bbdec3fdd1edb5754f4c421098e1 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 15:18:24 -0700 Subject: restyle remaining modules to match project style For this change I decided to bundle the remaining modules that need to be resytyled because only two were large enough to warrant doing on their own. Reduces lint errors from 225 to 162 (-28%). Line count from 3073 to 2465 (-20%). Another step in resolving issue #2902. --- src/util.cpp | 80 ++-- src/util.h | 92 ++-- src/wcstringutil.cpp | 23 +- src/wcstringutil.h | 31 +- src/wgetopt.cpp | 640 ++++++++++--------------- src/wgetopt.h | 297 +++++------- src/wildcard.cpp | 1303 +++++++++++++++++++++----------------------------- src/wildcard.h | 115 ++--- src/wutil.cpp | 406 ++++++---------- src/wutil.h | 141 ++---- 10 files changed, 1260 insertions(+), 1868 deletions(-) (limited to 'src') diff --git a/src/util.cpp b/src/util.cpp index 9d084af6..316bac68 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,39 +1,32 @@ -/** \file util.c - Generic utilities library. - - Contains datastructures such as automatically growing array lists, priority queues, etc. -*/ +// Generic utilities library. +// +// Contains data structures such as automatically growing array lists, priority queues, etc. +#include #include +#include +#include #include #include -#include -#include -#include +#include "common.h" #include "fallback.h" // IWYU pragma: keep #include "util.h" -#include "common.h" #include "wutil.h" // IWYU pragma: keep -int wcsfilecmp(const wchar_t *a, const wchar_t *b) -{ +int wcsfilecmp(const wchar_t *a, const wchar_t *b) { CHECK(a, 0); CHECK(b, 0); - if (*a==0) - { - if (*b==0) - return 0; + if (*a == 0) { + if (*b == 0) return 0; return -1; } - if (*b==0) - { + if (*b == 0) { return 1; } - long secondary_diff=0; - if (iswdigit(*a) && iswdigit(*b)) - { + long secondary_diff = 0; + if (iswdigit(*a) && iswdigit(*b)) { wchar_t *aend, *bend; long al; long bl; @@ -43,53 +36,40 @@ int wcsfilecmp(const wchar_t *a, const wchar_t *b) al = wcstol(a, &aend, 10); bl = wcstol(b, &bend, 10); - if (errno) - { - /* - Huuuuuuuuge numbers - fall back to regular string comparison - */ + if (errno) { + // Huge numbers - fall back to regular string comparison. return wcscmp(a, b); } diff = al - bl; - if (diff) - return diff > 0 ? 2 : -2; + if (diff) return diff > 0 ? 2 : -2; - secondary_diff = (aend-a) - (bend-b); + secondary_diff = (aend - a) - (bend - b); - a=aend-1; - b=bend-1; - } - else - { + a = aend - 1; + b = bend - 1; + } else { int diff = towlower(*a) - towlower(*b); - if (diff != 0) - return (diff>0)?2:-2; + if (diff != 0) return (diff > 0) ? 2 : -2; - secondary_diff = *a-*b; + secondary_diff = *a - *b; } - int res = wcsfilecmp(a+1, b+1); + int res = wcsfilecmp(a + 1, b + 1); - if (abs(res) < 2) - { - /* - No primary difference in rest of string. - Use secondary difference on this element if found. - */ - if (secondary_diff) - { - return secondary_diff > 0 ? 1 :-1; + if (abs(res) < 2) { + // No primary difference in rest of string. Use secondary difference on this element if + // found. + if (secondary_diff) { + return secondary_diff > 0 ? 1 : -1; } } return res; } -long long get_time() -{ +long long get_time() { struct timeval time_struct; gettimeofday(&time_struct, 0); - return 1000000ll*time_struct.tv_sec+time_struct.tv_usec; + return 1000000ll * time_struct.tv_sec + time_struct.tv_usec; } - diff --git a/src/util.h b/src/util.h index d073ef83..9d2e4bbb 100644 --- a/src/util.h +++ b/src/util.h @@ -1,67 +1,49 @@ -/** \file util.h - Generic utilities library. -*/ - +// Generic utilities library. #ifndef FISH_UTIL_H #define FISH_UTIL_H -/** - Returns the larger of two ints -*/ -template -inline T maxi(T a, T b) -{ - return a>b?a:b; +/// Returns the larger of two ints. +template +inline T maxi(T a, T b) { + return a > b ? a : b; } -/** - Returns the smaller of two ints - */ -template -inline T mini(T a, T b) -{ - return a +inline T mini(T a, T b) { + return a < b ? a : b; } -/** - Compares two wide character strings with an (arguably) intuitive - ordering. - - This function tries to order strings in a way which is intuitive to - humans with regards to sorting strings containing numbers. - - Most sorting functions would sort the strings 'file1.txt' - 'file5.txt' and 'file12.txt' as: - - file1.txt - file12.txt - file5.txt - - This function regards any sequence of digits as a single entity - when performing comparisons, so the output is instead: - - file1.txt - file5.txt - file12.txt - - Which most people would find more intuitive. - - This won't return the optimum results for numbers in bases higher - than ten, such as hexadecimal, but at least a stable sort order - will result. - - This function performs a two-tiered sort, where difference in case - and in number of leading zeroes in numbers only have effect if no - other differences between strings are found. This way, a 'file1' - and 'File1' will not be considered identical, and hence their - internal sort order is not arbitrary, but the names 'file1', - 'File2' and 'file3' will still be sorted in the order given above. -*/ +/// Compares two wide character strings with an (arguably) intuitive ordering. This function tries +/// to order strings in a way which is intuitive to humans with regards to sorting strings +/// containing numbers. +/// +/// Most sorting functions would sort the strings 'file1.txt' 'file5.txt' and 'file12.txt' as: +/// +/// file1.txt +/// file12.txt +/// file5.txt +/// +/// This function regards any sequence of digits as a single entity when performing comparisons, so +/// the output is instead: +/// +/// file1.txt +/// file5.txt +/// file12.txt +/// +/// Which most people would find more intuitive. +/// +/// This won't return the optimum results for numbers in bases higher than ten, such as hexadecimal, +/// but at least a stable sort order will result. +/// +/// This function performs a two-tiered sort, where difference in case and in number of leading +/// zeroes in numbers only have effect if no other differences between strings are found. This way, +/// a 'file1' and 'File1' will not be considered identical, and hence their internal sort order is +/// not arbitrary, but the names 'file1', 'File2' and 'file3' will still be sorted in the order +/// given above. int wcsfilecmp(const wchar_t *a, const wchar_t *b); -/** - Get the current time in microseconds since Jan 1, 1970 -*/ +/// Get the current time in microseconds since Jan 1, 1970. long long get_time(); #endif diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp index b752409b..3983eb53 100644 --- a/src/wcstringutil.cpp +++ b/src/wcstringutil.cpp @@ -1,24 +1,18 @@ -/** \file wcstringutil.cpp - -Helper functions for working with wcstring -*/ -#include "common.h" +// Helper functions for working with wcstring. #include "wcstringutil.h" +#include "common.h" typedef wcstring::size_type size_type; -wcstring_range wcstring_tok(wcstring& str, const wcstring &needle, wcstring_range last) -{ +wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, wcstring_range last) { size_type pos = last.second == wcstring::npos ? wcstring::npos : last.first; if (pos != wcstring::npos && last.second != wcstring::npos) pos += last.second; if (pos != wcstring::npos && pos != 0) ++pos; - if (pos == wcstring::npos || pos >= str.size()) - { + if (pos == wcstring::npos || pos >= str.size()) { return std::make_pair(wcstring::npos, wcstring::npos); } - if (needle.empty()) - { + if (needle.empty()) { return std::make_pair(pos, wcstring::npos); } @@ -26,12 +20,9 @@ wcstring_range wcstring_tok(wcstring& str, const wcstring &needle, wcstring_rang if (pos == wcstring::npos) return std::make_pair(wcstring::npos, wcstring::npos); size_type next_pos = str.find_first_of(needle, pos); - if (next_pos == wcstring::npos) - { + if (next_pos == wcstring::npos) { return std::make_pair(pos, wcstring::npos); - } - else - { + } else { str[next_pos] = L'\0'; return std::make_pair(pos, next_pos - pos); } diff --git a/src/wcstringutil.h b/src/wcstringutil.h index d2feec07..ea4489e7 100644 --- a/src/wcstringutil.h +++ b/src/wcstringutil.h @@ -1,7 +1,4 @@ -/** \file wcstringutil.h - -Helper functions for working with wcstring -*/ +// Helper functions for working with wcstring. #ifndef FISH_WCSTRINGUTIL_H #define FISH_WCSTRINGUTIL_H @@ -10,21 +7,19 @@ Helper functions for working with wcstring #include "common.h" -/** - typedef that represents a range in a wcstring. - The first element is the location, the second is the count. -*/ +/// Typedef that represents a range in a wcstring. The first element is the location, the second is +/// the count. typedef std::pair wcstring_range; -/** - wcstring equivalent of wcstok(). Supports NUL. - For convenience and wcstok() compatibility, the first character of each - token separator is replaced with NUL. - Returns a pair of (pos, count). - Returns (npos, npos) when it's done. - Returns (pos, npos) when the token is already known to be the final token. - Note that the final token may not necessarily return (pos, npos). -*/ -wcstring_range wcstring_tok(wcstring& str, const wcstring &needle, wcstring_range last = wcstring_range(0,0)); +/// wcstring equivalent of wcstok(). Supports NUL. For convenience and wcstok() compatibility, the +/// first character of each token separator is replaced with NUL. +/// +/// Returns a pair of (pos, count). +/// Returns (npos, npos) when it's done. +/// Returns (pos, npos) when the token is already known to be the final token. +/// +/// Note that the final token may not necessarily return (pos, npos). +wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, + wcstring_range last = wcstring_range(0, 0)); #endif diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index 02476718..d85b5e74 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -1,85 +1,68 @@ -/** \file wgetopt.c - A version of the getopt library for use with wide character strings. - - This is simply the gnu getopt library, but converted for use with - wchar_t instead of char. This is not usually useful since the argv - array is always defined to be of type char**, but in fish, all - internal commands use wide characters and hence this library is - useful. - - If you want to use this version of getopt in your program, - download the fish sourcecode, available at the fish homepage. Extract - the sourcode, copy wgetopt.c and wgetopt.h into your program - directory, include wgetopt.h in your program, and use all the - regular getopt functions, prefixing every function, global - variable and structure with a 'w', and use only wide character - strings. There are no other functional changes in this version of - getopt besides using wide character strings. - - For examples of how to use wgetopt, see the fish builtin - functions, many of which are defined in builtin.c. - -*/ - - -/* Getopt for GNU. - NOTE: getopt is now part of the C library, so if you don't know what - "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu - before changing it! - - Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 - Free Software Foundation, Inc. - - This file is part of the GNU C Library. Its master source is NOT part of - the C library, however. The master source lives in /gd/gnu/lib. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If - not, write to the Free Software Foundation, Inc., 675 Mass Ave, - Cambridge, MA 02139, USA. */ - +// A version of the getopt library for use with wide character strings. +// +// This is simply the gnu getopt library, but converted for use with wchar_t instead of char. This +// is not usually useful since the argv array is always defined to be of type char**, but in fish, +// all internal commands use wide characters and hence this library is useful. +// +// If you want to use this version of getopt in your program, download the fish sourcecode, +// available at the fish homepage. Extract the sourcode, copy +// wgetopt.c and wgetopt.h into your program directory, include wgetopt.h in your program, and use +// all the regular getopt functions, prefixing every function, global variable and structure with a +// 'w', and use only wide character strings. There are no other functional changes in this version +// of getopt besides using wide character strings. +// +// For examples of how to use wgetopt, see the fish builtin functions, many of which are defined in +// builtin.c. + +// Getopt for GNU. +// +// NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space +// clean" means, talk to roland@gnu.ai.mit.edu before changing it! +// +// Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 +// Free Software Foundation, Inc. +// +// This file is part of the GNU C Library. Its master source is NOT part of the C library, however. +// The master source lives in /gd/gnu/lib. +// +// The GNU C Library is free software; you can redistribute it and/or modify it under the terms of +// the GNU Library General Public License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// The GNU C Library 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 Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public License along with the GNU C +// Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass +// Ave, Cambridge, MA 02139, USA. #include "config.h" #include #include -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ +// This needs to come after some library #include to get __GNU_LIBRARY__ defined. +#ifdef __GNU_LIBRARY__ +// Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting +// prototypes for getopt. #include -#endif /* GNU C library. */ - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - +#endif // GNU C library. + +// This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves +// differently for the user, since it allows the user to intersperse the options with the other +// arguments. +// +// As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options +// precede everything else. Thus all application programs are extended to handle flexible argument +// order. +// +// GNU application programs can use a third alternative mode in which they can distinguish the +// relative order of options and other arguments. #include "common.h" +#include "fallback.h" // IWYU pragma: keep #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep -#include "fallback.h" // IWYU pragma: keep -/** - Use translation functions if available -*/ +// Use translation functions if available. #ifdef _ #undef _ #endif @@ -94,229 +77,178 @@ #define _(wstr) wstr #endif -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -#include // IWYU pragma: keep -#define my_index wcschr +#ifdef __GNU_LIBRARY__ +// We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can +// cause trouble. On some systems, it contains special magic macros that don't work in GCC. +#include // IWYU pragma: keep +#define my_index wcschr #else -/* Avoid depending on library functions or files - whose names are inconsistent. */ +// Avoid depending on library functions or files whose names are inconsistent. char *getenv(); -static wchar_t * -my_index(const wchar_t *str, int chr) -{ - while (*str) - { - if (*str == chr) - return (wchar_t *) str; +static wchar_t *my_index(const wchar_t *str, int chr) { + while (*str) { + if (*str == chr) return (wchar_t *)str; str++; } return 0; } -/* If using GCC, we can safely declare strlen this way. - If not using GCC, it is ok not to declare it. */ +// If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare +// it. #ifdef __GNUC__ -/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. - That was relevant to code that was here before. */ -#if !defined (__STDC__) || !__STDC__ -/* gcc with -traditional declares the built-in strlen to return int, - and has done so at least since version 2.4.5. -- rms. */ +// Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that +// was here before. +#if !defined(__STDC__) || !__STDC__ +// gcc with -traditional declares the built-in strlen to return int, and has done so at least since +// version 2.4.5. -- rms. extern int wcslen(const wchar_t *); -#endif /* not __STDC__ */ -#endif /* __GNUC__ */ - -#endif /* not __GNU_LIBRARY__ */ - - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,woptind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -void wgetopter_t::exchange(wchar_t **argv) -{ +#endif // not __STDC__ +#endif // __GNUC__ + +#endif // not __GNU_LIBRARY__ + +// Exchange two adjacent subsequences of ARGV. One subsequence is elements +// [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The +// other is elements [last_nonopt,woptind), which contains all the options processed since those +// non-options were skipped. +// +// `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the +// non-options in ARGV after they are moved. +void wgetopter_t::exchange(wchar_t **argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = woptind; wchar_t *tem; - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ + // Exchange the shorter segment with the far end of the longer segment. That puts the shorter + // segment into the right place. It leaves the longer segment in the right place overall, but it + // consists of two parts that need to be swapped next. - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ + while (top > middle && middle > bottom) { + if (top - middle > middle - bottom) { + // Bottom segment is the short one. int len = middle - bottom; int i; - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { + // Swap it with the top part of the top segment. + for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } - /* Exclude the moved bottom segment from further swapping. */ + // Exclude the moved bottom segment from further swapping. top -= len; - } - else - { - /* Top segment is the short one. */ + } else { + // Top segment is the short one. int len = top - middle; int i; - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { + // Swap it with the bottom part of the bottom segment. + for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } - /* Exclude the moved top segment from further swapping. */ + // Exclude the moved top segment from further swapping. bottom += len; } } - /* Update records for the slots the non-options now occupy. */ - + // Update records for the slots the non-options now occupy. first_nonopt += (woptind - last_nonopt); last_nonopt = woptind; } -/* Initialize the internal data when the first call is made. */ - -const wchar_t * wgetopter_t::_wgetopt_initialize(const wchar_t *optstring) -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - +// Initialize the internal data when the first call is made. +const wchar_t *wgetopter_t::_wgetopt_initialize(const wchar_t *optstring) { + // Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the + // sequence of previously skipped non-option ARGV-elements is empty. first_nonopt = last_nonopt = woptind = 1; - nextchar = NULL; - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { + // Determine how to handle the ordering of options and nonoptions. + if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; - } - else if (optstring[0] == '+') - { + } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; - } - else + } else ordering = PERMUTE; return optstring; } -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `woptind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns `EOF'. - Then `woptind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `wopterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `w.woptarg', otherwise `w.woptarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only) -{ +// Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. +// +// If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option +// element. The characters of this element (aside from the initial '-') are option characters. If +// `getopt' is called repeatedly, it returns successively each of the option characters from each of +// the option elements. +// +// If `getopt' finds another option character, it returns that character, updating `woptind' and +// `nextchar' so that the next call to `getopt' can resume the scan with the following option +// character or ARGV-element. +// +// If there are no more option characters, `getopt' returns `EOF'. Then `woptind' is the index in +// ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so +// that those that are not options now come last.) +// +// OPTSTRING is a string containing the legitimate option characters. If an option character is seen +// that is not listed in OPTSTRING, return '?' after printing an error message. If you set +// `wopterr' to zero, the error message is suppressed but we still return '?'. +// +// If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text +// in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. +// Two colons mean an option that wants an optional arg; if there is text in the current +// ARGV-element, it is returned in `w.woptarg', otherwise `w.woptarg' is set to zero. +// +// If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option +// ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. +// +// Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the +// abbreviation is unique or is an exact match for some defined option. If they have an argument, +// it follows the option name in the same ARGV-element, separated from the option name by a `=', or +// else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that +// option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is +// zero. +// +// LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. +// +// LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a +// long-named option has been found by the most recent call. +// +// If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. +int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, + const struct woption *longopts, int *longind, int long_only) { woptarg = NULL; - if (woptind == 0) - optstring = _wgetopt_initialize(optstring); - - if (nextchar == NULL || *nextchar == '\0') - { - /* Advance to the next ARGV-element. */ - - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ + if (woptind == 0) optstring = _wgetopt_initialize(optstring); + if (nextchar == NULL || *nextchar == '\0') { + // Advance to the next ARGV-element. + if (ordering == PERMUTE) { + // If we have just processed some options following some non-options, exchange them so + // that the options come first. if (first_nonopt != last_nonopt && last_nonopt != woptind) exchange(argv); else if (last_nonopt != woptind) first_nonopt = woptind; - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (woptind < argc - && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) + // Skip any additional non-options and extend the range of non-options previously + // skipped. + while (woptind < argc && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) woptind++; last_nonopt = woptind; } - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (woptind != argc && !wcscmp(argv[woptind], L"--")) - { + // The special ARGV-element `--' means premature end of options. Skip it like a null option, + // then exchange with previous non-options as if it were an option, then skip everything + // else like a non-option. + if (woptind != argc && !wcscmp(argv[woptind], L"--")) { woptind++; if (first_nonopt != last_nonopt && last_nonopt != woptind) @@ -328,241 +260,187 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts woptind = argc; } - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ + // If we have done all the ARGV-elements, stop the scan and back over any non-options that + // we skipped and permuted. - if (woptind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - woptind = first_nonopt; + if (woptind == argc) { + // Set the next-arg-index to point at the non-options that we previously skipped, so the + // caller will digest them. + if (first_nonopt != last_nonopt) woptind = first_nonopt; return EOF; } - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if ((argv[woptind][0] != '-' || argv[woptind][1] == '\0')) - { - if (ordering == REQUIRE_ORDER) - return EOF; + // If we have come to a non-option and did not permute it, either stop the scan or describe + // it to the caller and pass it by. + if ((argv[woptind][0] != '-' || argv[woptind][1] == '\0')) { + if (ordering == REQUIRE_ORDER) return EOF; woptarg = argv[woptind++]; return 1; } - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - nextchar = (argv[woptind] + 1 - + (longopts != NULL && argv[woptind][1] == '-')); + // We have found another option-ARGV-element. Skip the initial punctuation. + nextchar = (argv[woptind] + 1 + (longopts != NULL && argv[woptind][1] == '-')); } - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[woptind][1] == '-' - || (long_only && (argv[woptind][2] || !my_index(optstring, argv[woptind][1]))))) - { + // Decode the current option-ARGV-element. + + // Check whether the ARGV-element is a long option. + // + // If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't + // consider it an abbreviated form of a long option that starts with f. Otherwise there would + // be no way to give the -f short option. + // + // On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do + // consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg + // "u". + // + // This distinction seems to be the most useful approach. + if (longopts != NULL && + (argv[woptind][1] == '-' || + (long_only && (argv[woptind][2] || !my_index(optstring, argv[woptind][1]))))) { wchar_t *nameend; const struct woption *p; const struct woption *pfound = NULL; int exact = 0; int ambig = 0; - int indfound = 0; /* set to zero by Anton */ + int indfound = 0; // set to zero by Anton int option_index; - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */; - /* Test all long options for either exact match - or abbreviated matches. */ + // Test all long options for either exact match or abbreviated matches. for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!wcsncmp(p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int)(nameend - nextchar) == (unsigned int)wcslen(p->name)) - { - /* Exact match found. */ + if (!wcsncmp(p->name, nextchar, nameend - nextchar)) { + if ((unsigned int)(nameend - nextchar) == (unsigned int)wcslen(p->name)) { + // Exact match found. pfound = p; indfound = option_index; exact = 1; break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ + } else if (pfound == NULL) { + // First nonexact match found. pfound = p; indfound = option_index; - } - else - /* Second or later nonexact match found. */ + } else + // Second or later nonexact match found. ambig = 1; } - if (ambig && !exact) - { + if (ambig && !exact) { if (wopterr) - fwprintf(stderr, _(L"%ls: Option '%ls' is ambiguous\n"), - argv[0], argv[woptind]); + fwprintf(stderr, _(L"%ls: Option '%ls' is ambiguous\n"), argv[0], argv[woptind]); nextchar += wcslen(nextchar); woptind++; return '?'; } - if (pfound != NULL) - { + if (pfound != NULL) { option_index = indfound; woptind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ + if (*nameend) { + // Don't test has_arg with >, because some C compilers don't allow it to be used on + // enums. if (pfound->has_arg) woptarg = nameend + 1; - else - { - if (wopterr) - { - if (argv[woptind - 1][1] == '-') - /* --option */ - fwprintf(stderr, - _(L"%ls: Option '--%ls' doesn't allow an argument\n"), + else { + if (wopterr) { + if (argv[woptind - 1][1] == '-') // --option + fwprintf(stderr, _(L"%ls: Option '--%ls' doesn't allow an argument\n"), argv[0], pfound->name); else - /* +option or -option */ - fwprintf(stderr, - _(L"%ls: Option '%lc%ls' doesn't allow an argument\n"), + // +option or -option + fwprintf(stderr, _(L"%ls: Option '%lc%ls' doesn't allow an argument\n"), argv[0], argv[woptind - 1][0], pfound->name); } nextchar += wcslen(nextchar); return '?'; } - } - else if (pfound->has_arg == 1) - { + } else if (pfound->has_arg == 1) { if (woptind < argc) woptarg = argv[woptind++]; - else - { + else { if (wopterr) - fwprintf(stderr, _(L"%ls: Option '%ls' requires an argument\n"), - argv[0], argv[woptind - 1]); + fwprintf(stderr, _(L"%ls: Option '%ls' requires an argument\n"), argv[0], + argv[woptind - 1]); nextchar += wcslen(nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += wcslen(nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { + if (longind != NULL) *longind = option_index; + if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[woptind][1] == '-' - || my_index(optstring, *nextchar) == NULL) - { - if (wopterr) - { - if (argv[woptind][1] == '-') - /* --option */ - fwprintf(stderr, _(L"%ls: Unrecognized option '--%ls'\n"), - argv[0], nextchar); + // Can't find it as a long option. If this is not getopt_long_only, or the option starts + // with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a + // short option. + if (!long_only || argv[woptind][1] == '-' || my_index(optstring, *nextchar) == NULL) { + if (wopterr) { + if (argv[woptind][1] == '-') // --option + fwprintf(stderr, _(L"%ls: Unrecognized option '--%ls'\n"), argv[0], nextchar); else - /* +option or -option */ - fwprintf(stderr, _(L"%ls: Unrecognized option '%lc%ls'\n"), - argv[0], argv[woptind][0], nextchar); + // +option or -option + fwprintf(stderr, _(L"%ls: Unrecognized option '%lc%ls'\n"), argv[0], + argv[woptind][0], nextchar); } - nextchar = (wchar_t *) L""; + nextchar = (wchar_t *)L""; woptind++; return '?'; } } - /* Look at and handle the next short option-character. */ - + // Look at and handle the next short option-character. { wchar_t c = *nextchar++; - wchar_t *temp = const_cast(my_index(optstring, c)); + wchar_t *temp = const_cast(my_index(optstring, c)); - /* Increment `woptind' when we start to process its last character. */ - if (*nextchar == '\0') - ++woptind; + // Increment `woptind' when we start to process its last character. + if (*nextchar == '\0') ++woptind; - if (temp == NULL || c == ':') - { - if (wopterr) - { + if (temp == NULL || c == ':') { + if (wopterr) { fwprintf(stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], (wint_t)c); } woptopt = c; - if (*nextchar != '\0') - woptind++; + if (*nextchar != '\0') woptind++; return '?'; } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { + if (temp[1] == ':') { + if (temp[2] == ':') { + // This is an option that accepts an argument optionally. + if (*nextchar != '\0') { woptarg = nextchar; woptind++; - } - else + } else woptarg = NULL; nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { + } else { + // This is an option that requires an argument. + if (*nextchar != '\0') { woptarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ + // If we end this ARGV-element by taking the rest as an arg, we must advance to + // the next element now. woptind++; - } - else if (woptind == argc) - { - if (wopterr) - { - /* 1003.2 specifies the format of this message. */ - fwprintf(stderr, _(L"%ls: Option requires an argument -- %lc\n"), - argv[0], (wint_t)c); + } else if (woptind == argc) { + if (wopterr) { + // 1003.2 specifies the format of this message. + fwprintf(stderr, _(L"%ls: Option requires an argument -- %lc\n"), argv[0], + (wint_t)c); } woptopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; - } - else - /* We already incremented `woptind' once; - increment it again when taking next ARGV-elt as argument. */ + } else + // We already incremented `woptind' once; increment it again when taking next + // ARGV-elt as argument. woptarg = argv[woptind++]; nextchar = NULL; } @@ -571,12 +449,12 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts } } -int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index) -{ +int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, + const struct woption *long_options, int *opt_index) { return _wgetopt_internal(argc, argv, options, long_options, opt_index, 0); } -int wgetopter_t::wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index) -{ +int wgetopter_t::wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, + const struct woption *long_options, int *opt_index) { return _wgetopt_internal(argc, argv, options, long_options, opt_index, 1); } diff --git a/src/wgetopt.h b/src/wgetopt.h index d20529bb..95e1462d 100644 --- a/src/wgetopt.h +++ b/src/wgetopt.h @@ -1,27 +1,18 @@ -/** \file wgetopt.h - A version of the getopt library for use with wide character strings. - - This is simply the gnu getopt library, but converted for use with - wchar_t instead of char. This is not usually useful since the argv - array is always defined to be of type char**, but in fish, all - internal commands use wide characters and hence this library is - useful. - - If you want to use this version of getopt in your program, - download the fish sourcecode, available at the fish homepage. Extract - the sourcode, copy wgetopt.c and wgetopt.h into your program - directory, include wgetopt.h in your program, and use all the - regular getopt functions, prefixing every function, global - variable and structure with a 'w', and use only wide character - strings. There are no other functional changes in this version of - getopt besides using wide character strings. - - For examples of how to use wgetopt, see the fish builtin - functions, many of which are defined in builtin.c. - -*/ - +// A version of the getopt library for use with wide character strings. +// +// This is simply the gnu getopt library, but converted for use with wchar_t instead of char. This +// is not usually useful since the argv array is always defined to be of type char**, but in fish, +// all internal commands use wide characters and hence this library is useful. +// +// If you want to use this version of getopt in your program, download the fish sourcecode, +// available at the fish homepage. Extract the sourcode, copy +// wgetopt.c and wgetopt.h into your program directory, include wgetopt.h in your program, and use +// all the regular getopt functions, prefixing every function, global variable and structure with a +// 'w', and use only wide character strings. There are no other functional changes in this version +// of getopt besides using wide character strings. +// +// For examples of how to use wgetopt, see the fish builtin functions, many of which are defined in +// builtin.c. /* Declarations for getopt. Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc. @@ -49,169 +40,131 @@ Cambridge, MA 02139, USA. */ #include -class wgetopter_t -{ -private: +class wgetopter_t { + private: void exchange(wchar_t **argv); - const wchar_t * _wgetopt_initialize(const wchar_t *optstring); - int _wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only); - -public: - /* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - + const wchar_t *_wgetopt_initialize(const wchar_t *optstring); + int _wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, + const struct woption *longopts, int *longind, int long_only); + + public: + // For communication from `getopt' to the caller. When `getopt' finds an option that takes an + // argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each + // non-option ARGV-element is returned here. wchar_t *woptarg; - - /* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `woptind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - - /* XXX 1003.2 says this must be 1 before any call. */ + + // Index in ARGV of the next element to be scanned. This is used for communication to and from + // the caller and for communication between successive calls to `getopt'. + // + // On entry to `getopt', zero means this is the first call; initialize. + // + // When `getopt' returns EOF, this is the index of the first of the non-option elements that the + // caller should itself scan. + // + // Otherwise, `woptind' communicates from one call to the next how much of ARGV has been scanned + // so far. + + // XXX 1003.2 says this must be 1 before any call. int woptind; - - - /* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - + + // The next char to be scanned in the option-element in which the last option character we + // returned was found. This allows us to pick up the scan where we left off. + // + // If this is zero, or a null string, it means resume the scan by advancing to the next + // ARGV-element. wchar_t *nextchar; - - /* Callers store zero here to inhibit the error message - for unrecognized options. */ - + + // Callers store zero here to inhibit the error message for unrecognized options. int wopterr; - - /* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - + + // Set to an option character which was unrecognized. This must be initialized on some systems + // to avoid linking in the system's own getopt implementation. int woptopt; - - /* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is PERMUTE. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by using `+' as the first - character of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return EOF with `woptind' != ARGC. */ - - enum - { - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER - } ordering; - - /* Handle permutation of arguments. */ - - /* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - + + // Describe how to deal with options that follow non-option ARGV-elements. + // + // If the caller did not specify anything, the default is PERMUTE. + // + // REQUIRE_ORDER means don't recognize them as options; stop option processing when the first + // non-option is seen. This is what Unix does. This mode of operation is selected by using `+' + // as the first character of the list of option characters. + // + // PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all + // the non-options are at the end. This allows options to be given in any order, even with + // programs that were not written to expect this. + // + // RETURN_IN_ORDER is an option available to programs that were written to expect options and + // other ARGV-elements in any order and that care about the ordering of the two. We describe + // each non-option ARGV-element as if it were the argument of an option with character code 1. + // Using `-' as the first character of the list of option characters selects this mode of + // operation. + // + // The special argument `--' forces an end of option-scanning regardless of the value of + // `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with + // `woptind' != ARGC. + enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; + + // Handle permutation of arguments. + + // Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' + // is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. int first_nonopt; int last_nonopt; - - - wgetopter_t() : woptarg(NULL), woptind(0), nextchar(0), wopterr(0), woptopt('?'), ordering(), first_nonopt(0), last_nonopt(0) - { - } - - int wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index); - int wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index); + + wgetopter_t() + : woptarg(NULL), + woptind(0), + nextchar(0), + wopterr(0), + woptopt('?'), + ordering(), + first_nonopt(0), + last_nonopt(0) {} + + int wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, + const struct woption *long_options, int *opt_index); + int wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, + const struct woption *long_options, int *opt_index); }; -/** Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct woption -{ - /** - long name for switch - */ +/// Describe the long-named options requested by the application. The LONG_OPTIONS argument to +/// getopt_long or getopt_long_only is a vector of `struct option' terminated by an element +/// containing a name which is zero. +/// +/// The field `has_arg' is: +/// no_argument (or 0) if the option does not take an argument, +/// required_argument (or 1) if the option requires an argument, +/// optional_argument (or 2) if the option takes an optional argument. +/// +/// If the field `flag' is not NULL, it points to a variable that is set to the value given in the +/// field `val' when the option is found, but left unchanged if the option is not found. +/// +/// To have a long-named option do something other than set an `int' to a compiled-in constant, such +/// as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a +/// nonzero value (the equivalent single-letter option character, if there is one). For long +/// options that have a zero `flag' field, `getopt' returns the contents of the `val' field. +struct woption { + /// Long name for switch. const wchar_t *name; - /** - Must be one of no_argument, required_argument and - optional_argument. - - has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. - */ + /// Must be one of no_argument, required_argument and optional_argument. + /// + /// has_arg can't be an enum because some compilers complain about type mismatches in all the + /// code that assumes it is an int. int has_arg; - - /** - If non-null, the flag whose value should be set if this switch is encountered - */ + /// If non-null, the flag whose value should be set if this switch is encountered. int *flag; - - /** - If \c flag is non-null, this is the value that flag will be set - to. Otherwise, this is the return-value of the function call. - */ + /// If \c flag is non-null, this is the value that flag will be set to. Otherwise, this is the + /// return-value of the function call. int val; }; -/* Names for the values of the `has_arg' field of `struct option'. */ - -/** - Specifies that a switch does not accept an argument -*/ -#define no_argument 0 -/** - Specifies that a switch requires an argument -*/ -#define required_argument 1 -/** - Specifies that a switch accepts an optional argument -*/ -#define optional_argument 2 +// Names for the values of the `has_arg' field of `struct option'. + +/// Specifies that a switch does not accept an argument. +#define no_argument 0 +/// Specifies that a switch requires an argument. +#define required_argument 1 +/// Specifies that a switch accepts an optional argument. +#define optional_argument 2 #endif /* FISH_WGETOPT_H */ diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 471453f6..1a09d63e 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -1,113 +1,74 @@ -/** \file wildcard.c - -Fish needs it's own globbing implementation to support -tab-expansion of globbed parameters. Also provides recursive -wildcards using **. -*/ -#include -#include -#include +// Fish needs it's own globbing implementation to support tab-expansion of globbed parameters. Also +// provides recursive wildcards using **. +#include #include #include -#include -#include -#include -#include #include +#include #include +#include +#include #include +#include +#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep #include "common.h" -#include "wildcard.h" #include "complete.h" -#include "reader.h" #include "expand.h" +#include "fallback.h" // IWYU pragma: keep +#include "reader.h" +#include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep -/** - Description for generic executable -*/ -#define COMPLETE_EXEC_DESC _( L"Executable" ) -/** - Description for link to executable -*/ -#define COMPLETE_EXEC_LINK_DESC _( L"Executable link" ) - -/** - Description for regular file -*/ -#define COMPLETE_FILE_DESC _( L"File" ) -/** - Description for character device -*/ -#define COMPLETE_CHAR_DESC _( L"Character device" ) -/** - Description for block device -*/ -#define COMPLETE_BLOCK_DESC _( L"Block device" ) -/** - Description for fifo buffer -*/ -#define COMPLETE_FIFO_DESC _( L"Fifo" ) -/** - Description for symlink -*/ -#define COMPLETE_SYMLINK_DESC _( L"Symbolic link" ) -/** - Description for symlink -*/ -#define COMPLETE_DIRECTORY_SYMLINK_DESC _( L"Symbolic link to directory" ) -/** - Description for Rotten symlink -*/ -#define COMPLETE_ROTTEN_SYMLINK_DESC _( L"Rotten symbolic link" ) -/** - Description for symlink loop -*/ -#define COMPLETE_LOOP_SYMLINK_DESC _( L"Symbolic link loop" ) -/** - Description for socket files -*/ -#define COMPLETE_SOCKET_DESC _( L"Socket" ) -/** - Description for directories -*/ -#define COMPLETE_DIRECTORY_DESC _( L"Directory" ) - -/* Finds an internal (ANY_STRING, etc.) style wildcard, or wcstring::npos */ -static size_t wildcard_find(const wchar_t *wc) -{ - for (size_t i=0; wc[i] != L'\0'; i++) - { - if (wc[i] == ANY_CHAR || wc[i] == ANY_STRING || wc[i] == ANY_STRING_RECURSIVE) - { +/// Description for generic executable. +#define COMPLETE_EXEC_DESC _(L"Executable") +/// Description for link to executable. +#define COMPLETE_EXEC_LINK_DESC _(L"Executable link") +/// Description for regular file. +#define COMPLETE_FILE_DESC _(L"File") +/// Description for character device. +#define COMPLETE_CHAR_DESC _(L"Character device") +/// Description for block device. +#define COMPLETE_BLOCK_DESC _(L"Block device") +/// Description for fifo buffer. +#define COMPLETE_FIFO_DESC _(L"Fifo") +/// Description for symlink. +#define COMPLETE_SYMLINK_DESC _(L"Symbolic link") +/// Description for symlink. +#define COMPLETE_DIRECTORY_SYMLINK_DESC _(L"Symbolic link to directory") +/// Description for Rotten symlink. +#define COMPLETE_ROTTEN_SYMLINK_DESC _(L"Rotten symbolic link") +/// Description for symlink loop. +#define COMPLETE_LOOP_SYMLINK_DESC _(L"Symbolic link loop") +/// Description for socket files. +#define COMPLETE_SOCKET_DESC _(L"Socket") +/// Description for directories. +#define COMPLETE_DIRECTORY_DESC _(L"Directory") + +/// Finds an internal (ANY_STRING, etc.) style wildcard, or wcstring::npos. +static size_t wildcard_find(const wchar_t *wc) { + for (size_t i = 0; wc[i] != L'\0'; i++) { + if (wc[i] == ANY_CHAR || wc[i] == ANY_STRING || wc[i] == ANY_STRING_RECURSIVE) { return i; } } return wcstring::npos; } -// Implementation of wildcard_has. Needs to take the length to handle embedded nulls (#1631) -static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) -{ +/// Implementation of wildcard_has. Needs to take the length to handle embedded nulls (issue #1631). +static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) { assert(str != NULL); const wchar_t *end = str + len; - if (internal) - { - for (; str < end; str++) - { + if (internal) { + for (; str < end; str++) { if ((*str == ANY_CHAR) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE)) return true; } - } - else - { - wchar_t prev=0; - for (; str < end; str++) - { - if (((*str == L'*') || (*str == L'?')) && (prev != L'\\')) - return true; + } else { + wchar_t prev = 0; + for (; str < end; str++) { + if (((*str == L'*') || (*str == L'?')) && (prev != L'\\')) return true; prev = *str; } } @@ -115,141 +76,107 @@ static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) return false; } -bool wildcard_has(const wchar_t *str, bool internal) -{ +bool wildcard_has(const wchar_t *str, bool internal) { assert(str != NULL); return wildcard_has_impl(str, wcslen(str), internal); } -bool wildcard_has(const wcstring &str, bool internal) -{ +bool wildcard_has(const wcstring &str, bool internal) { return wildcard_has_impl(str.data(), str.size(), internal); } - -/** - Check whether the string str matches the wildcard string wc. - - \param str String to be matched. - \param wc The wildcard. - \param is_first Whether files beginning with dots should not be matched against wildcards. -*/ -static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const wchar_t *wc, bool leading_dots_fail_to_match, bool is_first) -{ - if (*str == 0 && *wc==0) - { - /* We're done */ - return fuzzy_match_exact; - } - - /* Hackish fix for #270 . Prevent wildcards from matching . or .., but we must still allow literal matches. */ - if (leading_dots_fail_to_match && is_first && contains(str, L".", L"..")) - { - /* The string is '.' or '..'. Return true if the wildcard exactly matches. */ +/// Check whether the string str matches the wildcard string wc. +/// +/// \param str String to be matched. +/// \param wc The wildcard. +/// \param is_first Whether files beginning with dots should not be matched against wildcards. +static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const wchar_t *wc, + bool leading_dots_fail_to_match, + bool is_first) { + if (*str == 0 && *wc == 0) { + return fuzzy_match_exact; // we're done + } + + // Hackish fix for issue #270. Prevent wildcards from matching . or .., but we must still allow + // literal matches. + if (leading_dots_fail_to_match && is_first && contains(str, L".", L"..")) { + // The string is '.' or '..'. Return true if the wildcard exactly matches. return wcscmp(str, wc) ? fuzzy_match_none : fuzzy_match_exact; } - - if (*wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) - { - /* Ignore hidden file */ - if (leading_dots_fail_to_match && is_first && *str == L'.') - { + + if (*wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) { + // Ignore hidden file + if (leading_dots_fail_to_match && is_first && *str == L'.') { return fuzzy_match_none; } - - /* Common case of * at the end. In that case we can early out since we know it will match. */ - if (wc[1] == L'\0') - { + + // Common case of * at the end. In that case we can early out since we know it will match. + if (wc[1] == L'\0') { return fuzzy_match_exact; } - /* Try all submatches */ - do - { - enum fuzzy_match_type_t subresult = wildcard_match_internal(str, wc+1, leading_dots_fail_to_match, false); - if (subresult != fuzzy_match_none) - { + // Try all submatches. + do { + enum fuzzy_match_type_t subresult = + wildcard_match_internal(str, wc + 1, leading_dots_fail_to_match, false); + if (subresult != fuzzy_match_none) { return subresult; } } while (*str++ != 0); return fuzzy_match_none; - } - else if (*str == 0) - { - /* - End of string, but not end of wildcard, and the next wildcard - element is not a '*', so this is not a match. - */ + } else if (*str == 0) { + // End of string, but not end of wildcard, and the next wildcard element is not a '*', so + // this is not a match. return fuzzy_match_none; - } - else if (*wc == ANY_CHAR) - { - if (is_first && *str == L'.') - { + } else if (*wc == ANY_CHAR) { + if (is_first && *str == L'.') { return fuzzy_match_none; } - return wildcard_match_internal(str+1, wc+1, leading_dots_fail_to_match, false); - } - else if (*wc == *str) - { - return wildcard_match_internal(str+1, wc+1, leading_dots_fail_to_match, false); + return wildcard_match_internal(str + 1, wc + 1, leading_dots_fail_to_match, false); + } else if (*wc == *str) { + return wildcard_match_internal(str + 1, wc + 1, leading_dots_fail_to_match, false); } return fuzzy_match_none; } - -/* This does something horrible refactored from an even more horrible function */ -static wcstring resolve_description(wcstring *completion, const wchar_t *explicit_desc, wcstring(*desc_func)(const wcstring &)) -{ +// This does something horrible refactored from an even more horrible function. +static wcstring resolve_description(wcstring *completion, const wchar_t *explicit_desc, + wcstring (*desc_func)(const wcstring &)) { size_t complete_sep_loc = completion->find(PROG_COMPLETE_SEP); - if (complete_sep_loc != wcstring::npos) - { - /* This completion has an embedded description, do not use the generic description */ + if (complete_sep_loc != wcstring::npos) { + // This completion has an embedded description, do not use the generic description. const wcstring description = completion->substr(complete_sep_loc + 1); completion->resize(complete_sep_loc); return description; - } - else - { + } else { const wcstring func_result = (desc_func ? desc_func(*completion) : wcstring()); - if (! func_result.empty()) - { + if (!func_result.empty()) { return func_result; - } - else - { + } else { return explicit_desc ? explicit_desc : L""; } } } -/* A transient parameter pack needed by wildcard_complete. */ -struct wc_complete_pack_t -{ - const wcstring &orig; // the original string, transient - const wchar_t *desc; // literal description - wcstring(*desc_func)(const wcstring &); // function for generating descriptions +// A transient parameter pack needed by wildcard_complete. +struct wc_complete_pack_t { + const wcstring &orig; // the original string, transient + const wchar_t *desc; // literal description + wcstring (*desc_func)(const wcstring &); // function for generating descriptions expand_flags_t expand_flags; - wc_complete_pack_t(const wcstring &str, const wchar_t *des, wcstring(*df)(const wcstring &), expand_flags_t fl) : - orig(str), - desc(des), - desc_func(df), - expand_flags(fl) - {} + wc_complete_pack_t(const wcstring &str, const wchar_t *des, wcstring (*df)(const wcstring &), + expand_flags_t fl) + : orig(str), desc(des), desc_func(df), expand_flags(fl) {} }; -/* Weirdly specific and non-reusable helper function that makes its one call site much clearer */ -static bool has_prefix_match(const std::vector *comps, size_t first) -{ - if (comps != NULL) - { +// Weirdly specific and non-reusable helper function that makes its one call site much clearer. +static bool has_prefix_match(const std::vector *comps, size_t first) { + if (comps != NULL) { const size_t after_count = comps->size(); - for (size_t j = first; j < after_count; j++) - { - if (comps->at(j).match.type <= fuzzy_match_prefix) - { + for (size_t j = first; j < after_count; j++) { + if (comps->at(j).match.type <= fuzzy_match_prefix) { return true; } } @@ -257,136 +184,116 @@ static bool has_prefix_match(const std::vector *comps, size_t firs return false; } -/** - Matches the string against the wildcard, and if the wildcard is a - possible completion of the string, the remainder of the string is - inserted into the out vector. - - We ignore ANY_STRING_RECURSIVE here. The consequence is that you cannot - tab complete ** wildcards. This is historic behavior. - */ -static bool wildcard_complete_internal(const wchar_t *str, - const wchar_t *wc, - const wc_complete_pack_t ¶ms, - complete_flags_t flags, - std::vector *out, - bool is_first_call = false) -{ +/// Matches the string against the wildcard, and if the wildcard is a possible completion of the +/// string, the remainder of the string is inserted into the out vector. +/// +/// We ignore ANY_STRING_RECURSIVE here. The consequence is that you cannot tab complete ** +/// wildcards. This is historic behavior. +static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, + const wc_complete_pack_t ¶ms, complete_flags_t flags, + std::vector *out, bool is_first_call = false) { assert(str != NULL); assert(wc != NULL); - - /* Maybe early out for hidden files. We require that the wildcard match these exactly (i.e. a dot); ANY_STRING not allowed */ - if (is_first_call && str[0] == L'.' && wc[0] != L'.') - { + + // Maybe early out for hidden files. We require that the wildcard match these exactly (i.e. a + // dot); ANY_STRING not allowed. + if (is_first_call && str[0] == L'.' && wc[0] != L'.') { return false; } - - /* Locate the next wildcard character position, e.g. ANY_CHAR or ANY_STRING */ + + // Locate the next wildcard character position, e.g. ANY_CHAR or ANY_STRING. const size_t next_wc_char_pos = wildcard_find(wc); - - /* Maybe we have no more wildcards at all. This includes the empty string. */ - if (next_wc_char_pos == wcstring::npos) - { + + // Maybe we have no more wildcards at all. This includes the empty string. + if (next_wc_char_pos == wcstring::npos) { string_fuzzy_match_t match = string_fuzzy_match_string(wc, str); - - /* If we're allowing fuzzy match, any match is OK. Otherwise we require a prefix match. */ + + // If we're allowing fuzzy match, any match is OK. Otherwise we require a prefix match. bool match_acceptable; - if (params.expand_flags & EXPAND_FUZZY_MATCH) - { + if (params.expand_flags & EXPAND_FUZZY_MATCH) { match_acceptable = match.type != fuzzy_match_none; - } - else - { + } else { match_acceptable = match_type_shares_prefix(match.type); } - - if (match_acceptable && out != NULL) - { - /* Wildcard complete */ - bool full_replacement = match_type_requires_full_replacement(match.type) || (flags & COMPLETE_REPLACES_TOKEN); - - /* If we are not replacing the token, be careful to only store the part of the string after the wildcard */ + + if (match_acceptable && out != NULL) { + // Wildcard complete. + bool full_replacement = match_type_requires_full_replacement(match.type) || + (flags & COMPLETE_REPLACES_TOKEN); + + // If we are not replacing the token, be careful to only store the part of the string + // after the wildcard. assert(!full_replacement || wcslen(wc) <= wcslen(str)); wcstring out_completion = full_replacement ? params.orig : str + wcslen(wc); wcstring out_desc = resolve_description(&out_completion, params.desc, params.desc_func); - - /* Note: out_completion may be empty if the completion really is empty, e.g. tab-completing 'foo' when a file 'foo' exists. */ + + // Note: out_completion may be empty if the completion really is empty, e.g. + // tab-completing 'foo' when a file 'foo' exists. complete_flags_t local_flags = flags | (full_replacement ? COMPLETE_REPLACES_TOKEN : 0); append_completion(out, out_completion, out_desc, local_flags, match); } return match_acceptable; - } - else if (next_wc_char_pos > 0) - { - /* Here we have a non-wildcard prefix. Note that we don't do fuzzy matching for stuff before a wildcard, so just do case comparison and then recurse. */ - if (wcsncmp(str, wc, next_wc_char_pos) == 0) - { - // Normal match - return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, flags, out); - } - else if (wcsncasecmp(str, wc, next_wc_char_pos) == 0) - { - // Case insensitive match - return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, flags | COMPLETE_REPLACES_TOKEN, out); - } - else - { - // No match + } else if (next_wc_char_pos > 0) { + // Here we have a non-wildcard prefix. Note that we don't do fuzzy matching for stuff before + // a wildcard, so just do case comparison and then recurse. + if (wcsncmp(str, wc, next_wc_char_pos) == 0) { + // Normal match. + return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, + flags, out); + } else if (wcsncasecmp(str, wc, next_wc_char_pos) == 0) { + // Case insensitive match. + return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, + flags | COMPLETE_REPLACES_TOKEN, out); + } else { + // No match. return false; } assert(0 && "Unreachable code reached"); - } - else - { - /* Our first character is a wildcard. */ + } else { + // Our first character is a wildcard. assert(next_wc_char_pos == 0); - switch (wc[0]) - { - case ANY_CHAR: - { - if (str[0] == L'\0') - { + switch (wc[0]) { + case ANY_CHAR: { + if (str[0] == L'\0') { return false; - } - else - { + } else { return wildcard_complete_internal(str + 1, wc + 1, params, flags, out); } break; } - - case ANY_STRING: - { - /* Hackish. If this is the last character of the wildcard, then just complete with the empty string. This fixes cases like "f*" -> "f*o" */ - if (wc[1] == L'\0') - { + + case ANY_STRING: { + // Hackish. If this is the last character of the wildcard, then just complete with + // the empty string. This fixes cases like "f*" -> "f*o". + if (wc[1] == L'\0') { return wildcard_complete_internal(L"", L"", params, flags, out); } - - /* Try all submatches. #929: if the recursive call gives us a prefix match, just stop. This is sloppy - what we really want to do is say, once we've seen a match of a particular type, ignore all matches of that type further down the string, such that the wildcard produces the "minimal match.". */ + + // Try all submatches. Issue #929: if the recursive call gives us a prefix match, + // just stop. This is sloppy - what we really want to do is say, once we've seen a + // match of a particular type, ignore all matches of that type further down the + // string, such that the wildcard produces the "minimal match.". bool has_match = false; - for (size_t i=0; str[i] != L'\0'; i++) - { + for (size_t i = 0; str[i] != L'\0'; i++) { const size_t before_count = out ? out->size() : 0; - if (wildcard_complete_internal(str + i, wc + 1, params, flags, out)) - { - /* We found a match */ + if (wildcard_complete_internal(str + i, wc + 1, params, flags, out)) { + // We found a match. has_match = true; - - /* If out is NULL, we don't care about the actual matches. If out is not NULL but we have a prefix match, stop there. */ - if (out == NULL || has_prefix_match(out, before_count)) - { + + // If out is NULL, we don't care about the actual matches. If out is not + // NULL but we have a prefix match, stop there. + if (out == NULL || has_prefix_match(out, before_count)) { break; } } } return has_match; } - + case ANY_STRING_RECURSIVE: - /* We don't even try with this one */ + // We don't even try with this one. return false; - + default: assert(0 && "Unreachable code reached"); return false; @@ -395,160 +302,99 @@ static bool wildcard_complete_internal(const wchar_t *str, assert(0 && "Unreachable code reached"); } -bool wildcard_complete(const wcstring &str, - const wchar_t *wc, - const wchar_t *desc, - wcstring(*desc_func)(const wcstring &), - std::vector *out, - expand_flags_t expand_flags, - complete_flags_t flags) -{ - // Note out may be NULL +bool wildcard_complete(const wcstring &str, const wchar_t *wc, const wchar_t *desc, + wcstring (*desc_func)(const wcstring &), std::vector *out, + expand_flags_t expand_flags, complete_flags_t flags) { + // Note out may be NULL. assert(wc != NULL); wc_complete_pack_t params(str, desc, desc_func, expand_flags); return wildcard_complete_internal(str.c_str(), wc, params, flags, out, true /* first call */); } - -bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match) -{ - enum fuzzy_match_type_t match = wildcard_match_internal(str.c_str(), wc.c_str(), leading_dots_fail_to_match, true /* first */); +bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match) { + enum fuzzy_match_type_t match = wildcard_match_internal( + str.c_str(), wc.c_str(), leading_dots_fail_to_match, true /* first */); return match != fuzzy_match_none; } -/** - Obtain a description string for the file specified by the filename. - - The returned value is a string constant and should not be free'd. - - \param filename The file for which to find a description string - \param lstat_res The result of calling lstat on the file - \param lbuf The struct buf output of calling lstat on the file - \param stat_res The result of calling stat on the file - \param buf The struct buf output of calling stat on the file - \param err The errno value after a failed stat call on the file. - */ - -static wcstring file_get_desc(const wcstring &filename, - int lstat_res, - const struct stat &lbuf, - int stat_res, - const struct stat &buf, - int err) -{ - - if (!lstat_res) - { - if (S_ISLNK(lbuf.st_mode)) - { - if (!stat_res) - { - if (S_ISDIR(buf.st_mode)) - { +/// Obtain a description string for the file specified by the filename. +/// +/// The returned value is a string constant and should not be free'd. +/// +/// \param filename The file for which to find a description string +/// \param lstat_res The result of calling lstat on the file +/// \param lbuf The struct buf output of calling lstat on the file +/// \param stat_res The result of calling stat on the file +/// \param buf The struct buf output of calling stat on the file +/// \param err The errno value after a failed stat call on the file. +static wcstring file_get_desc(const wcstring &filename, int lstat_res, const struct stat &lbuf, + int stat_res, const struct stat &buf, int err) { + if (!lstat_res) { + if (S_ISLNK(lbuf.st_mode)) { + if (!stat_res) { + if (S_ISDIR(buf.st_mode)) { return COMPLETE_DIRECTORY_SYMLINK_DESC; - } - else - { - - if (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) - { - - if (waccess(filename, X_OK) == 0) - { - /* - Weird group permissions and other such - issues make it non-trivial to find out - if we can actually execute a file using - the result from stat. It is much safer - to use the access function, since it - tells us exactly what we want to know. - */ + } else { + if (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + if (waccess(filename, X_OK) == 0) { + // Weird group permissions and other such issues make it non-trivial to + // find out if we can actually execute a file using the result from + // stat. It is much safer to use the access function, since it tells us + // exactly what we want to know. return COMPLETE_EXEC_LINK_DESC; } } } - + return COMPLETE_SYMLINK_DESC; - - } - else - { - switch (err) - { - case ENOENT: - { + + } else { + switch (err) { + case ENOENT: { return COMPLETE_ROTTEN_SYMLINK_DESC; } - - case ELOOP: - { + case ELOOP: { return COMPLETE_LOOP_SYMLINK_DESC; } } - /* - On unknown errors we do nothing. The file will be - given the default 'File' description or one based on the suffix. - */ + // On unknown errors we do nothing. The file will be given the default 'File' + // description or one based on the suffix. } - - } - else if (S_ISCHR(buf.st_mode)) - { + + } else if (S_ISCHR(buf.st_mode)) { return COMPLETE_CHAR_DESC; - } - else if (S_ISBLK(buf.st_mode)) - { + } else if (S_ISBLK(buf.st_mode)) { return COMPLETE_BLOCK_DESC; - } - else if (S_ISFIFO(buf.st_mode)) - { + } else if (S_ISFIFO(buf.st_mode)) { return COMPLETE_FIFO_DESC; - } - else if (S_ISSOCK(buf.st_mode)) - { + } else if (S_ISSOCK(buf.st_mode)) { return COMPLETE_SOCKET_DESC; - } - else if (S_ISDIR(buf.st_mode)) - { + } else if (S_ISDIR(buf.st_mode)) { return COMPLETE_DIRECTORY_DESC; - } - else - { - if (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXGRP)) - { - - if (waccess(filename, X_OK) == 0) - { - /* - Weird group permissions and other such issues - make it non-trivial to find out if we can - actually execute a file using the result from - stat. It is much safer to use the access - function, since it tells us exactly what we want - to know. - */ + } else { + if (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXGRP)) { + if (waccess(filename, X_OK) == 0) { + // Weird group permissions and other such issues make it non-trivial to find out + // if we can actually execute a file using the result from stat. It is much + // safer to use the access function, since it tells us exactly what we want to + // know. return COMPLETE_EXEC_DESC; } } } } - - return COMPLETE_FILE_DESC ; + + return COMPLETE_FILE_DESC; } -/** Test if the given file is an executable (if EXECUTABLES_ONLY) or directory (if DIRECTORIES_ONLY). - If it matches, call wildcard_complete() with some description that we make up. - Note that the filename came from a readdir() call, so we know it exists. - */ -static bool wildcard_test_flags_then_complete(const wcstring &filepath, - const wcstring &filename, - const wchar_t *wc, - expand_flags_t expand_flags, - std::vector *out) -{ - /* Check if it will match before stat() */ - if (! wildcard_complete(filename, wc, NULL, NULL, NULL, expand_flags, 0)) - { +/// Test if the given file is an executable (if EXECUTABLES_ONLY) or directory (if +/// DIRECTORIES_ONLY). If it matches, call wildcard_complete() with some description that we make +/// up. Note that the filename came from a readdir() call, so we know it exists. +static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wcstring &filename, + const wchar_t *wc, expand_flags_t expand_flags, + std::vector *out) { + // Check if it will match before stat(). + if (!wildcard_complete(filename, wc, NULL, NULL, NULL, expand_flags, 0)) { return false; } @@ -556,199 +402,160 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath, int stat_res = -1; int stat_errno = 0; int lstat_res = lwstat(filepath, &lstat_buf); - if (lstat_res < 0) - { - /* lstat failed */ - } - else - { - if (S_ISLNK(lstat_buf.st_mode)) - { + if (lstat_res < 0) { + // lstat failed. + } else { + if (S_ISLNK(lstat_buf.st_mode)) { stat_res = wstat(filepath, &stat_buf); - - if (stat_res < 0) - { - /* - In order to differentiate between e.g. rotten symlinks - and symlink loops, we also need to know the error status of wstat. - */ + + if (stat_res < 0) { + // In order to differentiate between e.g. rotten symlinks and symlink loops, we also + // need to know the error status of wstat. stat_errno = errno; } - } - else - { + } else { stat_buf = lstat_buf; stat_res = lstat_res; } } - + const long long file_size = stat_res == 0 ? stat_buf.st_size : 0; const bool is_directory = stat_res == 0 && S_ISDIR(stat_buf.st_mode); const bool is_executable = stat_res == 0 && S_ISREG(stat_buf.st_mode); - - if (expand_flags & DIRECTORIES_ONLY) - { - if (!is_directory) - { + + if (expand_flags & DIRECTORIES_ONLY) { + if (!is_directory) { return false; } } - if (expand_flags & EXECUTABLES_ONLY) - { - if (!is_executable || waccess(filepath, X_OK) != 0) - { + if (expand_flags & EXECUTABLES_ONLY) { + if (!is_executable || waccess(filepath, X_OK) != 0) { return false; } } - - /* Compute the description */ + + // Compute the description. wcstring desc; - if (!(expand_flags & EXPAND_NO_DESCRIPTIONS)) - { + if (!(expand_flags & EXPAND_NO_DESCRIPTIONS)) { desc = file_get_desc(filepath, lstat_res, lstat_buf, stat_res, stat_buf, stat_errno); - - if (file_size >= 0) - { - if (!desc.empty()) - desc.append(L", "); + + if (file_size >= 0) { + if (!desc.empty()) desc.append(L", "); desc.append(format_size(file_size)); } } - - /* Append a / if this is a directory. Note this requirement may be the only reason we have to call stat() in some cases. */ - if (is_directory) - { - return wildcard_complete(filename + L'/', wc, desc.c_str(), NULL, out, expand_flags, COMPLETE_NO_SPACE); - } - else - { + + // Append a / if this is a directory. Note this requirement may be the only reason we have to + // call stat() in some cases. + if (is_directory) { + return wildcard_complete(filename + L'/', wc, desc.c_str(), NULL, out, expand_flags, + COMPLETE_NO_SPACE); + } else { return wildcard_complete(filename, wc, desc.c_str(), NULL, out, expand_flags, 0); } } -class wildcard_expander_t -{ - /* Prefix, i.e. effective working directory */ +class wildcard_expander_t { + // Prefix, i.e. effective working directory. const wcstring prefix; - - /* The original base we are expanding */ + // The original base we are expanding. const wcstring original_base; - - /* Original wildcard we are expanding. */ - const wchar_t * const original_wildcard; - - /* the set of items we have resolved, used to efficiently avoid duplication */ + // Original wildcard we are expanding. + const wchar_t *const original_wildcard; + // The set of items we have resolved, used to efficiently avoid duplication. std::set completion_set; - - /* the set of file IDs we have visited, used to avoid symlink loops */ + // The set of file IDs we have visited, used to avoid symlink loops. std::set visited_files; - - /* flags controlling expansion */ + // Flags controlling expansion. const expand_flags_t flags; - - /* resolved items get inserted into here. This is transient of course. */ + // Resolved items get inserted into here. This is transient of course. std::vector *resolved_completions; - - /* whether we have been interrupted */ + // Whether we have been interrupted. bool did_interrupt; - - /* whether we have successfully added any completions */ + // Whether we have successfully added any completions. bool did_add; - - /* We are a trailing slash - expand at the end */ + + /// We are a trailing slash - expand at the end. void expand_trailing_slash(const wcstring &base_dir); - - /* Given a directory base_dir, which is opened as base_dir_fp, expand an intermediate segment of the wildcard. - Treat ANY_STRING_RECURSIVE as ANY_STRING. - wc_segment is the wildcard segment for this directory - wc_remainder is the wildcard for subdirectories - */ - void expand_intermediate_segment(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, const wchar_t *wc_remainder); - - /* Given a directory base_dir, which is opened as base_dir_fp, expand an intermediate literal segment. - Use a fuzzy matching algorithm. - */ - void expand_literal_intermediate_segment_with_fuzz(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, const wchar_t *wc_remainder); - - /* Given a directory base_dir, which is opened as base_dir_fp, expand the last segment of the wildcard. - Treat ANY_STRING_RECURSIVE as ANY_STRING. - wc is the wildcard segment to use for matching - wc_remainder is the wildcard for subdirectories - */ + + /// Given a directory base_dir, which is opened as base_dir_fp, expand an intermediate segment + /// of the wildcard. Treat ANY_STRING_RECURSIVE as ANY_STRING. wc_segment is the wildcard + /// segment for this directory wc_remainder is the wildcard for subdirectories + void expand_intermediate_segment(const wcstring &base_dir, DIR *base_dir_fp, + const wcstring &wc_segment, const wchar_t *wc_remainder); + + /// Given a directory base_dir, which is opened as base_dir_fp, expand an intermediate literal + /// segment. Use a fuzzy matching algorithm. + void expand_literal_intermediate_segment_with_fuzz(const wcstring &base_dir, DIR *base_dir_fp, + const wcstring &wc_segment, + const wchar_t *wc_remainder); + + /// Given a directory base_dir, which is opened as base_dir_fp, expand the last segment of the + /// wildcard. Treat ANY_STRING_RECURSIVE as ANY_STRING. wc is the wildcard segment to use for + /// matching wc_remainder is the wildcard for subdirectories. void expand_last_segment(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc); - - /* Indicate whether we should cancel wildcard expansion. This latches 'interrupt' */ - bool interrupted() - { - if (! did_interrupt) - { - did_interrupt = (is_main_thread() ? reader_interrupted() : reader_thread_job_is_stale()); + + /// Indicate whether we should cancel wildcard expansion. This latches 'interrupt'. + bool interrupted() { + if (!did_interrupt) { + did_interrupt = + (is_main_thread() ? reader_interrupted() : reader_thread_job_is_stale()); } return did_interrupt; } - - void add_expansion_result(const wcstring &result) - { - /* This function is only for the non-completions case */ - assert(! (this->flags & EXPAND_FOR_COMPLETIONS)); - if (this->completion_set.insert(result).second) - { + + void add_expansion_result(const wcstring &result) { + // This function is only for the non-completions case. + assert(!(this->flags & EXPAND_FOR_COMPLETIONS)); + if (this->completion_set.insert(result).second) { append_completion(this->resolved_completions, result); this->did_add = true; } } - - /* Given a start point as an absolute path, for any directory that has exactly one non-hidden entity in it which is itself a directory, return that. The result is a relative path. For example, if start_point is '/usr' we may return 'local/bin/'. - - The result does not have a leading slash, but does have a trailing slash if non-empty. */ - wcstring descend_unique_hierarchy(const wcstring &start_point) - { - assert(! start_point.empty() && start_point.at(0) == L'/'); - + + // Given a start point as an absolute path, for any directory that has exactly one non-hidden + // entity in it which is itself a directory, return that. The result is a relative path. For + // example, if start_point is '/usr' we may return 'local/bin/'. + // + // The result does not have a leading slash, but does have a trailing slash if non-empty. + wcstring descend_unique_hierarchy(const wcstring &start_point) { + assert(!start_point.empty() && start_point.at(0) == L'/'); + wcstring unique_hierarchy; wcstring abs_unique_hierarchy = start_point; - + bool stop_descent = false; DIR *dir; - while (!stop_descent && (dir = wopendir(abs_unique_hierarchy))) - { - /* We keep track of the single unique_entry entry. If we get more than one, it's not unique and we stop the descent. */ + while (!stop_descent && (dir = wopendir(abs_unique_hierarchy))) { + // We keep track of the single unique_entry entry. If we get more than one, it's not + // unique and we stop the descent. wcstring unique_entry; - + bool child_is_dir; wcstring child_entry; - while (wreaddir_resolving(dir, abs_unique_hierarchy, child_entry, &child_is_dir)) - { - if (child_entry.empty() || child_entry.at(0) == L'.') - { - /* Either hidden, or . and .. entries. Skip them. */ - continue; - } - else if (child_is_dir && unique_entry.empty()) - { - /* First candidate */ - unique_entry = child_entry; - } - else - { - /* We either have two or more candidates, or the child is not a directory. We're done. */ + while (wreaddir_resolving(dir, abs_unique_hierarchy, child_entry, &child_is_dir)) { + if (child_entry.empty() || child_entry.at(0) == L'.') { + continue; // either hidden, or . and .. entries -- skip them + } else if (child_is_dir && unique_entry.empty()) { + unique_entry = child_entry; // first candidate + } else { + // We either have two or more candidates, or the child is not a directory. We're + // done. stop_descent = true; break; } } - - /* We stop if we got two or more entries; also stop if we got zero. */ - if (unique_entry.empty()) - { + + // We stop if we got two or more entries; also stop if we got zero. + if (unique_entry.empty()) { stop_descent = true; } - - if (! stop_descent) - { - /* We have an entry in the unique hierarchy! */ + + if (!stop_descent) { + // We have an entry in the unique hierarchy! append_path_component(unique_hierarchy, unique_entry); unique_hierarchy.push_back(L'/'); - + append_path_component(abs_unique_hierarchy, unique_entry); abs_unique_hierarchy.push_back(L'/'); } @@ -757,121 +564,104 @@ class wildcard_expander_t return unique_hierarchy; } - - void try_add_completion_result(const wcstring &filepath, const wcstring &filename, const wcstring &wildcard) - { - /* This function is only for the completions case */ + void try_add_completion_result(const wcstring &filepath, const wcstring &filename, + const wcstring &wildcard) { + // This function is only for the completions case. assert(this->flags & EXPAND_FOR_COMPLETIONS); - + wcstring abs_path = this->prefix; append_path_component(abs_path, filepath); - + size_t before = this->resolved_completions->size(); - if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags, this->resolved_completions)) - { - /* Hack. We added this completion result based on the last component of the wildcard. - Prepend all prior components of the wildcard to each completion that replaces its token. */ + if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags, + this->resolved_completions)) { + // Hack. We added this completion result based on the last component of the wildcard. + // Prepend all prior components of the wildcard to each completion that replaces its + // token. size_t wc_len = wildcard.size(); size_t orig_wc_len = wcslen(this->original_wildcard); assert(wc_len <= orig_wc_len); const wcstring wc_base(this->original_wildcard, orig_wc_len - wc_len); - + size_t after = this->resolved_completions->size(); - for (size_t i=before; i < after; i++) - { + for (size_t i = before; i < after; i++) { completion_t &c = this->resolved_completions->at(i); c.prepend_token_prefix(wc_base); c.prepend_token_prefix(this->original_base); } - - /* Hack. Implement EXPAND_SPECIAL_FOR_CD by descending the deepest unique hierarchy we can, and then appending any components to each new result. */ - if (flags & EXPAND_SPECIAL_FOR_CD) - { + + // Hack. Implement EXPAND_SPECIAL_FOR_CD by descending the deepest unique hierarchy we + // can, and then appending any components to each new result. + if (flags & EXPAND_SPECIAL_FOR_CD) { wcstring unique_hierarchy = this->descend_unique_hierarchy(abs_path); - if (! unique_hierarchy.empty()) - { - for (size_t i=before; i < after; i++) - { + if (!unique_hierarchy.empty()) { + for (size_t i = before; i < after; i++) { completion_t &c = this->resolved_completions->at(i); c.completion.append(unique_hierarchy); } } } - + this->did_add = true; } } - - /* Helper to resolve using our prefix */ - DIR *open_dir(const wcstring &base_dir) const - { + + // Helper to resolve using our prefix. + DIR *open_dir(const wcstring &base_dir) const { wcstring path = this->prefix; append_path_component(path, base_dir); return wopendir(path); } - -public: - - wildcard_expander_t(const wcstring &pref, const wcstring &orig_base, const wchar_t *orig_wc, expand_flags_t f, std::vector *r) : - prefix(pref), - original_base(orig_base), - original_wildcard(orig_wc), - flags(f), - resolved_completions(r), - did_interrupt(false), - did_add(false) - { + + public: + wildcard_expander_t(const wcstring &pref, const wcstring &orig_base, const wchar_t *orig_wc, + expand_flags_t f, std::vector *r) + : prefix(pref), + original_base(orig_base), + original_wildcard(orig_wc), + flags(f), + resolved_completions(r), + did_interrupt(false), + did_add(false) { assert(resolved_completions != NULL); - - /* Insert initial completions into our set to avoid duplicates */ - for (std::vector::const_iterator iter = resolved_completions->begin(); iter != resolved_completions->end(); ++iter) - { + + // Insert initial completions into our set to avoid duplicates. + for (std::vector::const_iterator iter = resolved_completions->begin(); + iter != resolved_completions->end(); ++iter) { this->completion_set.insert(iter->completion); } } - - /* Do wildcard expansion. This is recursive. */ + + // Do wildcard expansion. This is recursive. void expand(const wcstring &base_dir, const wchar_t *wc); - - int status_code() const - { - if (this->did_interrupt) - { + + int status_code() const { + if (this->did_interrupt) { return -1; - } - else - { + } else { return this->did_add ? 1 : 0; } } }; -void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir) -{ - if (interrupted()) - { +void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir) { + if (interrupted()) { return; } - - if (! (flags & EXPAND_FOR_COMPLETIONS)) - { - /* Trailing slash and not accepting incomplete, e.g. `echo /tmp/`. Insert this file if it exists. */ - if (waccess(base_dir, F_OK) == 0) - { + + if (!(flags & EXPAND_FOR_COMPLETIONS)) { + // Trailing slash and not accepting incomplete, e.g. `echo /tmp/`. Insert this file if it + // exists. + if (waccess(base_dir, F_OK) == 0) { this->add_expansion_result(base_dir); } - } - else - { - /* Trailing slashes and accepting incomplete, e.g. `echo /tmp/`. Everything is added. */ + } else { + // Trailing slashes and accepting incomplete, e.g. `echo /tmp/`. Everything is added. DIR *dir = open_dir(base_dir); - if (dir) - { + if (dir) { wcstring next; - while (wreaddir(dir, next) && ! interrupted()) - { - if (! next.empty() && next.at(0) != L'.') - { + while (wreaddir(dir, next) && !interrupted()) { + if (!next.empty() && next.at(0) != L'.') { this->try_add_completion_result(base_dir + next, next, L""); } } @@ -880,239 +670,212 @@ void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir) } } -void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, const wchar_t *wc_remainder) -{ +void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir, DIR *base_dir_fp, + const wcstring &wc_segment, + const wchar_t *wc_remainder) { wcstring name_str; - while (!interrupted() && wreaddir_for_dirs(base_dir_fp, &name_str)) - { - /* Note that it's critical we ignore leading dots here, else we may descend into . and .. */ - if (! wildcard_match(name_str, wc_segment, true)) - { - /* Doesn't match the wildcard for this segment, skip it */ + while (!interrupted() && wreaddir_for_dirs(base_dir_fp, &name_str)) { + // Note that it's critical we ignore leading dots here, else we may descend into . and .. + if (!wildcard_match(name_str, wc_segment, true)) { + // Doesn't match the wildcard for this segment, skip it. continue; } - + wcstring full_path = base_dir + name_str; struct stat buf; - if (0 != wstat(full_path, &buf) || !S_ISDIR(buf.st_mode)) - { - /* We either can't stat it, or we did but it's not a directory */ + if (0 != wstat(full_path, &buf) || !S_ISDIR(buf.st_mode)) { + // We either can't stat it, or we did but it's not a directory. continue; } const file_id_t file_id = file_id_t::file_id_from_stat(&buf); - if (!this->visited_files.insert(file_id).second) - { - /* Symlink loop! This directory was already visited, so skip it */ + if (!this->visited_files.insert(file_id).second) { + // Symlink loop! This directory was already visited, so skip it. continue; } - /* We made it through. Perform normal wildcard expansion on this new directory, starting at our tail_wc, which includes the ANY_STRING_RECURSIVE guy. */ + // We made it through. Perform normal wildcard expansion on this new directory, starting at + // our tail_wc, which includes the ANY_STRING_RECURSIVE guy. full_path.push_back(L'/'); this->expand(full_path, wc_remainder); - /* Now remove the visited file. This is for #2414: only directories "beneath" us should be considered visited. */ + // Now remove the visited file. This is for #2414: only directories "beneath" us should be + // considered visited. this->visited_files.erase(file_id); } } - -void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, const wchar_t *wc_remainder) -{ - // This only works with tab completions - // Ordinary wildcard expansion should never go fuzzy + +void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz( + const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, + const wchar_t *wc_remainder) { + // This only works with tab completions. Ordinary wildcard expansion should never go fuzzy. wcstring name_str; - while (!interrupted() && wreaddir_for_dirs(base_dir_fp, &name_str)) - { - /* Don't bother with . and .. */ - if (contains(name_str, L".", L"..")) - { + while (!interrupted() && wreaddir_for_dirs(base_dir_fp, &name_str)) { + // Don't bother with . and .. + if (contains(name_str, L".", L"..")) { continue; } - - // Skip cases that don't match or match exactly - // The match-exactly case was handled directly in expand() + + // Skip cases that don't match or match exactly. The match-exactly case was handled directly + // in expand(). const string_fuzzy_match_t match = string_fuzzy_match_string(wc_segment, name_str); - if (match.type == fuzzy_match_none || match.type == fuzzy_match_exact) - { + if (match.type == fuzzy_match_none || match.type == fuzzy_match_exact) { continue; } - + wcstring new_full_path = base_dir + name_str; new_full_path.push_back(L'/'); struct stat buf; - if (0 != wstat(new_full_path, &buf) || !S_ISDIR(buf.st_mode)) - { + if (0 != wstat(new_full_path, &buf) || !S_ISDIR(buf.st_mode)) { /* We either can't stat it, or we did but it's not a directory */ continue; } - - // Ok, this directory matches. Recurse to it. - // Then perform serious surgery on each result! - // Each result was computed with a prefix of original_wildcard - // We need to replace our segment of that with our name_str - // We also have to mark the completion as replacing and fuzzy + + // Ok, this directory matches. Recurse to it. Then perform serious surgery on each result! + // Each result was computed with a prefix of original_wildcard. We need to replace our + // segment of that with our name_str. We also have to mark the completion as replacing and + // fuzzy. const size_t before = this->resolved_completions->size(); - + this->expand(new_full_path, wc_remainder); const size_t after = this->resolved_completions->size(); - + assert(before <= after); - for (size_t i=before; i < after; i++) - { + for (size_t i = before; i < after; i++) { completion_t *c = &this->resolved_completions->at(i); - // Mark the completion as replacing - if (!(c->flags & COMPLETE_REPLACES_TOKEN)) - { + // Mark the completion as replacing. + if (!(c->flags & COMPLETE_REPLACES_TOKEN)) { c->flags |= COMPLETE_REPLACES_TOKEN; c->prepend_token_prefix(this->original_wildcard); c->prepend_token_prefix(this->original_base); } - // Ok, it's now replacing and is prefixed with the segment base, plus our original wildcard - // Replace our segment with name_str - // Our segment starts at the length of the original wildcard, minus what we have left to process, minus the length of our segment - // This logic is way too picky. Need to clean this up. - // One possibility is to send the "resolved wildcard" along with the actual wildcard + // Ok, it's now replacing and is prefixed with the segment base, plus our original + // wildcard. Replace our segment with name_str. Our segment starts at the length of the + // original wildcard, minus what we have left to process, minus the length of our + // segment. This logic is way too picky. Need to clean this up. One possibility is to + // send the "resolved wildcard" along with the actual wildcard. const size_t original_wildcard_len = wcslen(this->original_wildcard); const size_t wc_remainder_len = wcslen(wc_remainder); const size_t segment_len = wc_segment.length(); assert(c->completion.length() >= original_wildcard_len); - const size_t segment_start = original_wildcard_len + this->original_base.size() - wc_remainder_len - wc_segment.length() - 1; // -1 for the slash after our segment + const size_t segment_start = original_wildcard_len + this->original_base.size() - + wc_remainder_len - wc_segment.length() - + 1; // -1 for the slash after our segment assert(segment_start < original_wildcard_len); assert(c->completion.substr(segment_start, segment_len) == wc_segment); c->completion.replace(segment_start, segment_len, name_str); - - // And every match must be made at least as fuzzy as ours - if (match.compare(c->match) > 0) - { - // Our match is fuzzier + + // And every match must be made at least as fuzzy as ours. + if (match.compare(c->match) > 0) { + // Our match is fuzzier. c->match = match; } } } } -void wildcard_expander_t::expand_last_segment(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc) -{ +void wildcard_expander_t::expand_last_segment(const wcstring &base_dir, DIR *base_dir_fp, + const wcstring &wc) { wcstring name_str; - while (wreaddir(base_dir_fp, name_str)) - { - if (flags & EXPAND_FOR_COMPLETIONS) - { + while (wreaddir(base_dir_fp, name_str)) { + if (flags & EXPAND_FOR_COMPLETIONS) { this->try_add_completion_result(base_dir + name_str, name_str, wc); - } - else - { - // Normal wildcard expansion, not for completions - if (wildcard_match(name_str, wc, true /* skip files with leading dots */)) - { + } else { + // Normal wildcard expansion, not for completions. + if (wildcard_match(name_str, wc, true /* skip files with leading dots */)) { this->add_expansion_result(base_dir + name_str); } } } } -/** - The real implementation of wildcard expansion is in this - function. Other functions are just wrappers around this one. - - This function traverses the relevant directory tree looking for - matches, and recurses when needed to handle wildcrards spanning - multiple components and recursive wildcards. - - Because this function calls itself recursively with substrings, - it's important that the parameters be raw pointers instead of wcstring, - which would be too expensive to construct for all substrings. - - Args: - base_dir: the "working directory" against which the wildcard is to be resolved - wc: the wildcard string itself, e.g. foo*bar/baz (where * is acutally ANY_CHAR) -*/ -void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc) -{ +/// The real implementation of wildcard expansion is in this function. Other functions are just +/// wrappers around this one. +/// +/// This function traverses the relevant directory tree looking for matches, and recurses when +/// needed to handle wildcrards spanning multiple components and recursive wildcards. +/// +/// Because this function calls itself recursively with substrings, it's important that the +/// parameters be raw pointers instead of wcstring, which would be too expensive to construct for +/// all substrings. +/// +/// Args: +/// base_dir: the "working directory" against which the wildcard is to be resolved +/// wc: the wildcard string itself, e.g. foo*bar/baz (where * is acutally ANY_CHAR) +void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc) { assert(wc != NULL); - - if (interrupted()) - { + + if (interrupted()) { return; } - - /* Get the current segment and compute interesting properties about it. */ + + // Get the current segment and compute interesting properties about it. const size_t wc_len = wcslen(wc); - const wchar_t * const next_slash = wcschr(wc, L'/'); + const wchar_t *const next_slash = wcschr(wc, L'/'); const bool is_last_segment = (next_slash == NULL); const size_t wc_segment_len = next_slash ? next_slash - wc : wc_len; const wcstring wc_segment = wcstring(wc, wc_segment_len); - const bool segment_has_wildcards = wildcard_has(wc_segment, true /* internal, i.e. look for ANY_CHAR instead of ? */); - - if (wc_segment.empty()) - { - /* Handle empty segment */ - assert(! segment_has_wildcards); - if (is_last_segment) - { + const bool segment_has_wildcards = + wildcard_has(wc_segment, true /* internal, i.e. look for ANY_CHAR instead of ? */); + + if (wc_segment.empty()) { + // Handle empty segment. + assert(!segment_has_wildcards); + if (is_last_segment) { this->expand_trailing_slash(base_dir); - } - else - { - /* Multiple adjacent slashes in the wildcard. Just skip them. */ + } else { + // Multiple adjacent slashes in the wildcard. Just skip them. this->expand(base_dir, next_slash + 1); } - } - else if (! segment_has_wildcards && ! is_last_segment) - { - /* Literal intermediate match. Note that we may not be able to actually read the directory (#2099) */ + } else if (!segment_has_wildcards && !is_last_segment) { + // Literal intermediate match. Note that we may not be able to actually read the directory + // (issue #2099). assert(next_slash != NULL); const wchar_t *wc_remainder = next_slash; - while (*wc_remainder == L'/') - { + while (*wc_remainder == L'/') { wc_remainder++; } - - /* This just trumps everything */ + + // This just trumps everything. size_t before = this->resolved_completions->size(); this->expand(base_dir + wc_segment + L'/', wc_remainder); - - /* Maybe try a fuzzy match (#94) if nothing was found with the literal match. Respect EXPAND_NO_DIRECTORY_ABBREVIATIONS (#2413). */ - bool allow_fuzzy = (this->flags & (EXPAND_FUZZY_MATCH | EXPAND_NO_FUZZY_DIRECTORIES)) == EXPAND_FUZZY_MATCH; - if (allow_fuzzy && this->resolved_completions->size() == before) - { + + // Maybe try a fuzzy match (#94) if nothing was found with the literal match. Respect + // EXPAND_NO_DIRECTORY_ABBREVIATIONS (issue #2413). + bool allow_fuzzy = (this->flags & (EXPAND_FUZZY_MATCH | EXPAND_NO_FUZZY_DIRECTORIES)) == + EXPAND_FUZZY_MATCH; + if (allow_fuzzy && this->resolved_completions->size() == before) { assert(this->flags & EXPAND_FOR_COMPLETIONS); DIR *base_dir_fd = open_dir(base_dir); - if (base_dir_fd != NULL) - { - this->expand_literal_intermediate_segment_with_fuzz(base_dir, base_dir_fd, wc_segment, wc_remainder); + if (base_dir_fd != NULL) { + this->expand_literal_intermediate_segment_with_fuzz(base_dir, base_dir_fd, + wc_segment, wc_remainder); closedir(base_dir_fd); } } - } - else - { - assert(! wc_segment.empty() && (segment_has_wildcards || is_last_segment)); + } else { + assert(!wc_segment.empty() && (segment_has_wildcards || is_last_segment)); DIR *dir = open_dir(base_dir); - if (dir) - { - if (is_last_segment) - { - /* Last wildcard segment, nonempty wildcard */ + if (dir) { + if (is_last_segment) { + // Last wildcard segment, nonempty wildcard. this->expand_last_segment(base_dir, dir, wc_segment); - } - else - { - /* Not the last segment, nonempty wildcard */ + } else { + // Not the last segment, nonempty wildcard. assert(next_slash != NULL); const wchar_t *wc_remainder = next_slash; - while (*wc_remainder == L'/') - { + while (*wc_remainder == L'/') { wc_remainder++; } this->expand_intermediate_segment(base_dir, dir, wc_segment, wc_remainder); } - - /* Recursive wildcards require special handling */ + + // Recursive wildcards require special handling. size_t asr_idx = wc_segment.find(ANY_STRING_RECURSIVE); - if (asr_idx != wcstring::npos) - { - /* Construct a "head + any" wildcard for matching stuff in this directory, and an "any + tail" wildcard for matching stuff in subdirectories. Note that the ANY_STRING_RECURSIVE character is present in both the head and the tail. */ + if (asr_idx != wcstring::npos) { + // Construct a "head + any" wildcard for matching stuff in this directory, and an + // "any + tail" wildcard for matching stuff in subdirectories. Note that the + // ANY_STRING_RECURSIVE character is present in both the head and the tail. const wcstring head_any(wc_segment, 0, asr_idx + 1); const wchar_t *any_tail = wc + asr_idx; assert(head_any.at(head_any.size() - 1) == ANY_STRING_RECURSIVE); @@ -1126,41 +889,45 @@ void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc) } } - -int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, expand_flags_t flags, std::vector *output) -{ +int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, + expand_flags_t flags, std::vector *output) { assert(output != NULL); - /* Fuzzy matching only if we're doing completions */ + // Fuzzy matching only if we're doing completions. assert((flags & (EXPAND_FUZZY_MATCH | EXPAND_FOR_COMPLETIONS)) != EXPAND_FUZZY_MATCH); - - /* EXPAND_SPECIAL_FOR_CD requires DIRECTORIES_ONLY and EXPAND_FOR_COMPLETIONS and EXPAND_NO_DESCRIPTIONS */ + + // EXPAND_SPECIAL_FOR_CD requires DIRECTORIES_ONLY and EXPAND_FOR_COMPLETIONS and + // EXPAND_NO_DESCRIPTIONS. assert(!(flags & EXPAND_SPECIAL_FOR_CD) || - ((flags & DIRECTORIES_ONLY) && (flags & EXPAND_FOR_COMPLETIONS) && (flags & EXPAND_NO_DESCRIPTIONS))); - - /* Hackish fix for 1631. We are about to call c_str(), which will produce a string truncated at any embedded nulls. We could fix this by passing around the size, etc. However embedded nulls are never allowed in a filename, so we just check for them and return 0 (no matches) if there is an embedded null. */ - if (wc.find(L'\0') != wcstring::npos) - { + ((flags & DIRECTORIES_ONLY) && (flags & EXPAND_FOR_COMPLETIONS) && + (flags & EXPAND_NO_DESCRIPTIONS))); + + // Hackish fix for issue #1631. We are about to call c_str(), which will produce a string + // truncated at any embedded nulls. We could fix this by passing around the size, etc. However + // embedded nulls are never allowed in a filename, so we just check for them and return 0 (no + // matches) if there is an embedded null. + if (wc.find(L'\0') != wcstring::npos) { return 0; } - - /* Compute the prefix and base dir. The prefix is what we prepend for filesystem operations (i.e. the working directory), the base_dir is the part of the wildcard consumed thus far, which we also have to append. The difference is that the base_dir is returned as part of the expansion, and the prefix is not. - - Check for a leading slash. If we find one, we have an absolute path: the prefix is empty, the base dir is /, and the wildcard is the remainder. If we don't find one, the prefix is the working directory, there's base dir is empty. - */ + + // Compute the prefix and base dir. The prefix is what we prepend for filesystem operations + // (i.e. the working directory), the base_dir is the part of the wildcard consumed thus far, + // which we also have to append. The difference is that the base_dir is returned as part of the + // expansion, and the prefix is not. + // + // Check for a leading slash. If we find one, we have an absolute path: the prefix is empty, the + // base dir is /, and the wildcard is the remainder. If we don't find one, the prefix is the + // working directory, there's base dir is empty. wcstring prefix, base_dir, effective_wc; - if (string_prefixes_string(L"/", wc)) - { + if (string_prefixes_string(L"/", wc)) { prefix = L""; base_dir = L"/"; effective_wc = wc.substr(1); - } - else - { + } else { prefix = working_directory; base_dir = L""; effective_wc = wc; } - + wildcard_expander_t expander(prefix, base_dir, effective_wc.c_str(), flags, output); expander.expand(base_dir, wc.c_str()); return expander.status_code(); diff --git a/src/wildcard.h b/src/wildcard.h index 05e185f4..1ed7cc89 100644 --- a/src/wildcard.h +++ b/src/wildcard.h @@ -1,84 +1,69 @@ -/** \file wildcard.h - - My own globbing implementation. Needed to implement this instead - of using libs globbing to support tab-expansion of globbed - paramaters. - -*/ +// My own globbing implementation. Needed to implement this instead of using libs globbing to +// support tab-expansion of globbed paramaters. #ifndef FISH_WILDCARD_H #define FISH_WILDCARD_H -#include #include +#include #include "common.h" -#include "expand.h" #include "complete.h" +#include "expand.h" -// Enumeration of all wildcard types -enum -{ - // Character representing any character except '/' (slash). +// Enumeration of all wildcard types. +enum { + /// Character representing any character except '/' (slash). ANY_CHAR = WILDCARD_RESERVED_BASE, - // Character representing any character string not containing '/' (slash). + /// Character representing any character string not containing '/' (slash). ANY_STRING, - // Character representing any character string. + /// Character representing any character string. ANY_STRING_RECURSIVE, - // This is a special psuedo-char that is not used other than to mark the - // end of the the special characters so we can sanity check the enum range. + /// This is a special psuedo-char that is not used other than to mark the + /// end of the the special characters so we can sanity check the enum range. ANY_SENTINAL }; -/** - Expand the wildcard by matching against the filesystem. - - New strings are allocated using malloc and should be freed by the caller. - - wildcard_expand works by dividing the wildcard into segments at - each directory boundary. Each segment is processed separatly. All - except the last segment are handled by matching the wildcard - segment against all subdirectories of matching directories, and - recursively calling wildcard_expand for matches. On the last - segment, matching is made to any file, and all matches are - inserted to the list. - - If wildcard_expand encounters any errors (such as insufficient - priviliges) during matching, no error messages will be printed and - wildcard_expand will continue the matching process. - - \param wc The wildcard string - \param working_directory The working directory - \param flags flags for the search. Can be any combination of EXPAND_FOR_COMPLETIONS and EXECUTABLES_ONLY - \param out The list in which to put the output - - \return 1 if matches where found, 0 otherwise. Return -1 on abort (I.e. ^C was pressed). - -*/ -int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, expand_flags_t flags, std::vector *out); - -/** - Test whether the given wildcard matches the string. Does not perform any I/O. - - \param str The string to test - \param wc The wildcard to test against - \param leading_dots_fail_to_match if set, strings with leading dots are assumed to be hidden files and are not matched - \return true if the wildcard matched -*/ -bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match = false); - -/** Check if the specified string contains wildcards */ +/// Expand the wildcard by matching against the filesystem. +/// +/// New strings are allocated using malloc and should be freed by the caller. +/// +/// wildcard_expand works by dividing the wildcard into segments at each directory boundary. Each +/// segment is processed separatly. All except the last segment are handled by matching the wildcard +/// segment against all subdirectories of matching directories, and recursively calling +/// wildcard_expand for matches. On the last segment, matching is made to any file, and all matches +/// are inserted to the list. +/// +/// If wildcard_expand encounters any errors (such as insufficient priviliges) during matching, no +/// error messages will be printed and wildcard_expand will continue the matching process. +/// +/// \param wc The wildcard string +/// \param working_directory The working directory +/// \param flags flags for the search. Can be any combination of EXPAND_FOR_COMPLETIONS and +/// EXECUTABLES_ONLY +/// \param out The list in which to put the output +/// +/// \return 1 if matches where found, 0 otherwise. Return -1 on abort (I.e. ^C was pressed). +int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, + expand_flags_t flags, std::vector *out); + +/// Test whether the given wildcard matches the string. Does not perform any I/O. +/// +/// \param str The string to test +/// \param wc The wildcard to test against +/// \param leading_dots_fail_to_match if set, strings with leading dots are assumed to be hidden +/// files and are not matched +/// +/// \return true if the wildcard matched +bool wildcard_match(const wcstring &str, const wcstring &wc, + bool leading_dots_fail_to_match = false); + +/// Check if the specified string contains wildcards. bool wildcard_has(const wcstring &, bool internal); bool wildcard_has(const wchar_t *, bool internal); -/** - Test wildcard completion -*/ -bool wildcard_complete(const wcstring &str, - const wchar_t *wc, - const wchar_t *desc, - wcstring(*desc_func)(const wcstring &), - std::vector *out, - expand_flags_t expand_flags, - complete_flags_t flags); +/// Test wildcard completion. +bool wildcard_complete(const wcstring &str, const wchar_t *wc, const wchar_t *desc, + wcstring (*desc_func)(const wcstring &), std::vector *out, + expand_flags_t expand_flags, complete_flags_t flags); #endif diff --git a/src/wutil.cpp b/src/wutil.cpp index 1cee8320..c8b9e3f4 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -1,30 +1,27 @@ -/** \file wutil.c - Wide character equivalents of various standard unix - functions. -*/ +// Wide character equivalents of various standard unix functions. #include "config.h" -#include -#include -#include -#include -#include +#include +#include #include #include -#include -#include -#include -#include #include +#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include "common.h" #include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep typedef std::string cstring; @@ -34,65 +31,53 @@ const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, #ifdef MAXPATHLEN #define PATH_MAX MAXPATHLEN #else -/** - Fallback length of MAXPATHLEN. Just a hopefully sane value... -*/ +/// Fallback length of MAXPATHLEN. Hopefully a sane value. #define PATH_MAX 4096 #endif #endif -/* Lock to protect wgettext */ +/// Lock to protect wgettext. static pthread_mutex_t wgettext_lock; -/* Map used as cache by wgettext. */ +/// Map used as cache by wgettext. typedef std::map wgettext_map_t; static wgettext_map_t wgettext_map; -bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir) -{ +bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, + bool *out_is_dir) { struct dirent *d = readdir(dir); if (!d) return false; out_name = str2wcstring(d->d_name); - if (out_is_dir) - { - /* The caller cares if this is a directory, so check */ + if (out_is_dir) { + // The caller cares if this is a directory, so check. bool is_dir = false; - - /* We may be able to skip stat, if the readdir can tell us the file type directly */ + + // We may be able to skip stat, if the readdir can tell us the file type directly. bool check_with_stat = true; #ifdef HAVE_STRUCT_DIRENT_D_TYPE - if (d->d_type == DT_DIR) - { - /* Known directory */ + if (d->d_type == DT_DIR) { + // Known directory. is_dir = true; check_with_stat = false; - } - else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) - { - /* We want to treat symlinks to directories as directories. Use stat to resolve it. */ + } else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) { + // We want to treat symlinks to directories as directories. Use stat to resolve it. check_with_stat = true; - } - else - { - /* Regular file */ + } else { + // Regular file. is_dir = false; check_with_stat = false; } -#endif // HAVE_STRUCT_DIRENT_D_TYPE - if (check_with_stat) - { - /* We couldn't determine the file type from the dirent; check by stat'ing it */ +#endif // HAVE_STRUCT_DIRENT_D_TYPE + if (check_with_stat) { + // We couldn't determine the file type from the dirent; check by stat'ing it. cstring fullpath = wcs2string(dir_path); fullpath.push_back('/'); fullpath.append(d->d_name); struct stat buf; - if (stat(fullpath.c_str(), &buf) != 0) - { + if (stat(fullpath.c_str(), &buf) != 0) { is_dir = false; - } - else - { + } else { is_dir = !!(S_ISDIR(buf.st_mode)); } } @@ -101,8 +86,7 @@ bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &ou return true; } -bool wreaddir(DIR *dir, std::wstring &out_name) -{ +bool wreaddir(DIR *dir, std::wstring &out_name) { struct dirent *d = readdir(dir); if (!d) return false; @@ -110,53 +94,45 @@ bool wreaddir(DIR *dir, std::wstring &out_name) return true; } -bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) -{ +bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { struct dirent *result = NULL; - while (result == NULL) - { + while (result == NULL) { struct dirent *d = readdir(dir); if (!d) break; - + #if HAVE_STRUCT_DIRENT_D_TYPE - switch (d->d_type) - { - // These may be directories + switch (d->d_type) { case DT_DIR: case DT_LNK: - case DT_UNKNOWN: + case DT_UNKNOWN: { + // These may be directories. result = d; break; - - // Nothing else can - default: + } + default: { + // Nothing else can. break; + } } #else - /* We can't determine if it's a directory or not, so just return it */ + // We can't determine if it's a directory or not, so just return it. result = d; #endif } - if (result && out_name) - { + if (result && out_name) { *out_name = str2wcstring(result->d_name); } return result != NULL; } - -const wcstring wgetcwd() -{ +const wcstring wgetcwd() { wcstring retval; char *res = getcwd(NULL, 0); - if (res) - { + if (res) { retval = str2wcstring(res); free(res); - } - else - { + } else { debug(0, _(L"getcwd() failed with errno %d/%s"), errno, strerror(errno)); retval = wcstring(); } @@ -164,176 +140,147 @@ const wcstring wgetcwd() return retval; } -int wchdir(const wcstring &dir) -{ +int wchdir(const wcstring &dir) { cstring tmp = wcs2string(dir); return chdir(tmp.c_str()); } -FILE *wfopen(const wcstring &path, const char *mode) -{ +FILE *wfopen(const wcstring &path, const char *mode) { int permissions = 0, options = 0; size_t idx = 0; - switch (mode[idx++]) - { - case 'r': + switch (mode[idx++]) { + case 'r': { permissions = O_RDONLY; break; - case 'w': + } + case 'w': { permissions = O_WRONLY; options = O_CREAT | O_TRUNC; break; - case 'a': + } + case 'a': { permissions = O_WRONLY; options = O_CREAT | O_APPEND; break; - default: + } + default: { errno = EINVAL; return NULL; break; + } } - /* Skip binary */ - if (mode[idx] == 'b') - idx++; + // Skip binary. + if (mode[idx] == 'b') idx++; - /* Consider append option */ - if (mode[idx] == '+') - permissions = O_RDWR; + // Consider append option. + if (mode[idx] == '+') permissions = O_RDWR; int fd = wopen_cloexec(path, permissions | options, 0666); - if (fd < 0) - return NULL; + if (fd < 0) return NULL; FILE *result = fdopen(fd, mode); - if (result == NULL) - close(fd); + if (result == NULL) close(fd); return result; } -bool set_cloexec(int fd) -{ +bool set_cloexec(int fd) { int flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) - { + if (flags < 0) { return false; - } - else if (flags & FD_CLOEXEC) - { + } else if (flags & FD_CLOEXEC) { return true; - } - else - { + } else { return fcntl(fd, F_SETFD, flags | FD_CLOEXEC) >= 0; } } -static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec) -{ +static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec) { ASSERT_IS_NOT_FORKED_CHILD(); cstring tmp = wcs2string(pathname); - /* Prefer to use O_CLOEXEC. It has to both be defined and nonzero. */ +// Prefer to use O_CLOEXEC. It has to both be defined and nonzero. #ifdef O_CLOEXEC - if (cloexec && (O_CLOEXEC != 0)) - { + if (cloexec && (O_CLOEXEC != 0)) { flags |= O_CLOEXEC; cloexec = false; } #endif int fd = ::open(tmp.c_str(), flags, mode); - if (cloexec && fd >= 0 && ! set_cloexec(fd)) - { + if (cloexec && fd >= 0 && !set_cloexec(fd)) { close(fd); fd = -1; } return fd; - } -int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) -{ +int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) { return wopen_internal(pathname, flags, mode, true); } -DIR *wopendir(const wcstring &name) -{ +DIR *wopendir(const wcstring &name) { const cstring tmp = wcs2string(name); return opendir(tmp.c_str()); } -int wstat(const wcstring &file_name, struct stat *buf) -{ +int wstat(const wcstring &file_name, struct stat *buf) { const cstring tmp = wcs2string(file_name); return stat(tmp.c_str(), buf); } -int lwstat(const wcstring &file_name, struct stat *buf) -{ +int lwstat(const wcstring &file_name, struct stat *buf) { const cstring tmp = wcs2string(file_name); return lstat(tmp.c_str(), buf); } -int waccess(const wcstring &file_name, int mode) -{ +int waccess(const wcstring &file_name, int mode) { const cstring tmp = wcs2string(file_name); return access(tmp.c_str(), mode); } -int wunlink(const wcstring &file_name) -{ +int wunlink(const wcstring &file_name) { const cstring tmp = wcs2string(file_name); return unlink(tmp.c_str()); } -void wperror(const wchar_t *s) -{ +void wperror(const wchar_t *s) { int e = errno; - if (s[0] != L'\0') - { + if (s[0] != L'\0') { fwprintf(stderr, L"%ls: ", s); } fwprintf(stderr, L"%s\n", strerror(e)); } -int make_fd_nonblocking(int fd) -{ +int make_fd_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); int err = 0; - if (!(flags & O_NONBLOCK)) - { + if (!(flags & O_NONBLOCK)) { err = fcntl(fd, F_SETFL, flags | O_NONBLOCK); } return err == -1 ? errno : 0; } -int make_fd_blocking(int fd) -{ +int make_fd_blocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); int err = 0; - if (flags & O_NONBLOCK) - { + if (flags & O_NONBLOCK) { err = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); } return err == -1 ? errno : 0; } -static inline void safe_append(char *buffer, const char *s, size_t buffsize) -{ +static inline void safe_append(char *buffer, const char *s, size_t buffsize) { strncat(buffer, s, buffsize - strlen(buffer) - 1); } -// In general, strerror is not async-safe, and therefore we cannot use it directly -// So instead we have to grub through sys_nerr and sys_errlist directly -// On GNU toolchain, this will produce a deprecation warning from the linker (!!), -// which appears impossible to suppress! -const char *safe_strerror(int err) -{ +// In general, strerror is not async-safe, and therefore we cannot use it directly. So instead we +// have to grub through sys_nerr and sys_errlist directly On GNU toolchain, this will produce a +// deprecation warning from the linker (!!), which appears impossible to suppress! +const char *safe_strerror(int err) { #if defined(__UCLIBC__) - // uClibc does not have sys_errlist, however, its strerror is believed to be async-safe - // See #808 + // uClibc does not have sys_errlist, however, its strerror is believed to be async-safe. + // See issue #808. return strerror(err); #elif defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST) #ifdef HAVE_SYS_ERRLIST - if (err >= 0 && err < sys_nerr && sys_errlist[err] != NULL) - { + if (err >= 0 && err < sys_nerr && sys_errlist[err] != NULL) { return sys_errlist[err]; } #elif defined(HAVE__SYS__ERRS) @@ -342,15 +289,15 @@ const char *safe_strerror(int err) extern int _sys_num_err; if (err >= 0 && err < _sys_num_err) { - return &_sys_errs[_sys_index[err]]; + return &_sys_errs[_sys_index[err]]; } -#endif // either HAVE__SYS__ERRS or HAVE_SYS_ERRLIST +#endif // either HAVE__SYS__ERRS or HAVE_SYS_ERRLIST else -#endif // defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST) +#endif // defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST) { int saved_err = errno; - /* Use a shared buffer for this case */ + // Use a shared buffer for this case. static char buff[384]; char errnum_buff[64]; format_long_safe(errnum_buff, err); @@ -365,16 +312,14 @@ const char *safe_strerror(int err) } } -void safe_perror(const char *message) -{ - // Note we cannot use strerror, because on Linux it uses gettext, which is not safe +void safe_perror(const char *message) { + // Note we cannot use strerror, because on Linux it uses gettext, which is not safe. int err = errno; char buff[384]; buff[0] = '\0'; - if (message) - { + if (message) { safe_append(buff, message, sizeof buff); safe_append(buff, ": ", sizeof buff); } @@ -387,23 +332,18 @@ void safe_perror(const char *message) #ifdef HAVE_REALPATH_NULL -wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) -{ +wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { cstring narrow_path = wcs2string(pathname); char *narrow_res = realpath(narrow_path.c_str(), NULL); - if (!narrow_res) - return NULL; + if (!narrow_res) return NULL; wchar_t *res; wcstring wide_res = str2wcstring(narrow_res); - if (resolved_path) - { + if (resolved_path) { wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX); res = resolved_path; - } - else - { + } else { res = wcsdup(wide_res.c_str()); } @@ -414,24 +354,19 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) #else -wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) -{ +wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { cstring tmp = wcs2string(pathname); char narrow_buff[PATH_MAX]; char *narrow_res = realpath(tmp.c_str(), narrow_buff); wchar_t *res; - if (!narrow_res) - return 0; + if (!narrow_res) return 0; const wcstring wide_res = str2wcstring(narrow_res); - if (resolved_path) - { + if (resolved_path) { wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX); res = resolved_path; - } - else - { + } else { res = wcsdup(wide_res.c_str()); } return res; @@ -439,9 +374,7 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) #endif - -wcstring wdirname(const wcstring &path) -{ +wcstring wdirname(const wcstring &path) { char *tmp = wcs2str(path.c_str()); char *narrow_res = dirname(tmp); wcstring result = format_string(L"%s", narrow_res); @@ -449,8 +382,7 @@ wcstring wdirname(const wcstring &path) return result; } -wcstring wbasename(const wcstring &path) -{ +wcstring wbasename(const wcstring &path) { char *tmp = wcs2str(path.c_str()); char *narrow_res = basename(tmp); wcstring result = format_string(L"%s", narrow_res); @@ -458,29 +390,24 @@ wcstring wbasename(const wcstring &path) return result; } -/* Really init wgettext */ -static void wgettext_really_init() -{ +// Really init wgettext. +static void wgettext_really_init() { pthread_mutex_init(&wgettext_lock, NULL); fish_bindtextdomain(PACKAGE_NAME, LOCALEDIR); fish_textdomain(PACKAGE_NAME); } -/** - For wgettext: Internal init function. Automatically called when a translation is first requested. -*/ -static void wgettext_init_if_necessary() -{ +/// For wgettext: Internal init function. Automatically called when a translation is first +/// requested. +static void wgettext_init_if_necessary() { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, wgettext_really_init); } -const wchar_t *wgettext(const wchar_t *in) -{ - if (!in) - return in; +const wchar_t *wgettext(const wchar_t *in) { + if (!in) return in; - // preserve errno across this since this is often used in printing error messages + // Preserve errno across this since this is often used in printing error messages. int err = errno; wgettext_init_if_necessary(); @@ -489,59 +416,51 @@ const wchar_t *wgettext(const wchar_t *in) scoped_lock lock(wgettext_lock); wcstring &val = wgettext_map[key]; - if (val.empty()) - { + if (val.empty()) { cstring mbs_in = wcs2string(key); char *out = fish_gettext(mbs_in.c_str()); val = format_string(L"%s", out); } errno = err; - // The returned string is stored in the map - // TODO: If we want to shrink the map, this would be a problem + // The returned string is stored in the map. + // TODO: If we want to shrink the map, this would be a problem. return val.c_str(); } -int wmkdir(const wcstring &name, int mode) -{ +int wmkdir(const wcstring &name, int mode) { cstring name_narrow = wcs2string(name); return mkdir(name_narrow.c_str(), mode); } -int wrename(const wcstring &old, const wcstring &newv) -{ +int wrename(const wcstring &old, const wcstring &newv) { cstring old_narrow = wcs2string(old); - cstring new_narrow =wcs2string(newv); + cstring new_narrow = wcs2string(newv); return rename(old_narrow.c_str(), new_narrow.c_str()); } -int fish_wcstoi(const wchar_t *str, wchar_t ** endptr, int base) -{ +int fish_wcstoi(const wchar_t *str, wchar_t **endptr, int base) { long ret = wcstol(str, endptr, base); - if (ret > INT_MAX) - { + if (ret > INT_MAX) { ret = INT_MAX; errno = ERANGE; - } - else if (ret < INT_MIN) - { + } else if (ret < INT_MIN) { ret = INT_MIN; errno = ERANGE; } return (int)ret; } -file_id_t file_id_t::file_id_from_stat(const struct stat *buf) -{ +file_id_t file_id_t::file_id_from_stat(const struct stat *buf) { assert(buf != NULL); - + file_id_t result = {}; result.device = buf->st_dev; result.inode = buf->st_ino; result.size = buf->st_size; result.change_seconds = buf->st_ctime; result.mod_seconds = buf->st_mtime; - + #if STAT_HAVE_NSEC result.change_nanoseconds = buf->st_ctime_nsec; result.mod_nanoseconds = buf->st_mtime_nsec; @@ -555,74 +474,53 @@ file_id_t file_id_t::file_id_from_stat(const struct stat *buf) result.change_nanoseconds = 0; result.mod_nanoseconds = 0; #endif - + return result; } - -file_id_t file_id_for_fd(int fd) -{ +file_id_t file_id_for_fd(int fd) { file_id_t result = kInvalidFileID; struct stat buf = {}; - if (0 == fstat(fd, &buf)) - { + if (0 == fstat(fd, &buf)) { result = file_id_t::file_id_from_stat(&buf); } return result; } -file_id_t file_id_for_path(const wcstring &path) -{ +file_id_t file_id_for_path(const wcstring &path) { file_id_t result = kInvalidFileID; struct stat buf = {}; - if (0 == wstat(path, &buf)) - { + if (0 == wstat(path, &buf)) { result = file_id_t::file_id_from_stat(&buf); } return result; - } -bool file_id_t::operator==(const file_id_t &rhs) const -{ - return this->compare_file_id(rhs) == 0; -} +bool file_id_t::operator==(const file_id_t &rhs) const { return this->compare_file_id(rhs) == 0; } -bool file_id_t::operator!=(const file_id_t &rhs) const -{ - return ! (*this == rhs); -} +bool file_id_t::operator!=(const file_id_t &rhs) const { return !(*this == rhs); } -template -int compare(T a, T b) -{ - if (a < b) - { +template +int compare(T a, T b) { + if (a < b) { return -1; - } - else if (a > b) - { + } else if (a > b) { return 1; } return 0; } -int file_id_t::compare_file_id(const file_id_t &rhs) const -{ - /* Compare each field, stopping when we get to a non-equal field */ +int file_id_t::compare_file_id(const file_id_t &rhs) const { + // Compare each field, stopping when we get to a non-equal field. int ret = 0; - if (! ret) ret = compare(device, rhs.device); - if (! ret) ret = compare(inode, rhs.inode); - if (! ret) ret = compare(size, rhs.size); - if (! ret) ret = compare(change_seconds, rhs.change_seconds); - if (! ret) ret = compare(change_nanoseconds, rhs.change_nanoseconds); - if (! ret) ret = compare(mod_seconds, rhs.mod_seconds); - if (! ret) ret = compare(mod_nanoseconds, rhs.mod_nanoseconds); + if (!ret) ret = compare(device, rhs.device); + if (!ret) ret = compare(inode, rhs.inode); + if (!ret) ret = compare(size, rhs.size); + if (!ret) ret = compare(change_seconds, rhs.change_seconds); + if (!ret) ret = compare(change_nanoseconds, rhs.change_nanoseconds); + if (!ret) ret = compare(mod_seconds, rhs.mod_seconds); + if (!ret) ret = compare(mod_nanoseconds, rhs.mod_nanoseconds); return ret; } - -bool file_id_t::operator<(const file_id_t &rhs) const -{ - return this->compare_file_id(rhs) < 0; -} +bool file_id_t::operator<(const file_id_t &rhs) const { return this->compare_file_id(rhs) < 0; } diff --git a/src/wutil.h b/src/wutil.h index a4fee900..9fc61096 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -1,141 +1,105 @@ -/** \file wutil.h - - Prototypes for wide character equivalents of various standard unix - functions. -*/ +// Prototypes for wide character equivalents of various standard unix functions. #ifndef FISH_WUTIL_H #define FISH_WUTIL_H -#include #include +#include +#include #include #include #include -#include #include "common.h" -/** - Wide character version of fopen(). This sets CLO_EXEC. -*/ +/// Wide character version of fopen(). This sets CLO_EXEC. FILE *wfopen(const wcstring &path, const char *mode); -/** Sets CLO_EXEC on a given fd */ +/// Sets CLO_EXEC on a given fd. bool set_cloexec(int fd); -/** Wide character version of open() that also sets the close-on-exec flag (atomically when possible). */ +/// Wide character version of open() that also sets the close-on-exec flag (atomically when +/// possible). int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode = 0); -/** Mark an fd as nonblocking; returns errno or 0 on success */ +/// Mark an fd as nonblocking; returns errno or 0 on success. int make_fd_nonblocking(int fd); -/** Mark an fd as blocking; returns errno or 0 on success */ +/// Mark an fd as blocking; returns errno or 0 on success. int make_fd_blocking(int fd); -/** Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by POSIX (hooray). */ +/// Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by +/// POSIX (hooray). DIR *wopendir(const wcstring &name); -/** - Wide character version of stat(). -*/ +/// Wide character version of stat(). int wstat(const wcstring &file_name, struct stat *buf); -/** - Wide character version of lstat(). -*/ +/// Wide character version of lstat(). int lwstat(const wcstring &file_name, struct stat *buf); -/** - Wide character version of access(). -*/ +/// Wide character version of access(). int waccess(const wcstring &pathname, int mode); -/** - Wide character version of unlink(). -*/ +/// Wide character version of unlink(). int wunlink(const wcstring &pathname); -/** - Wide character version of perror(). -*/ +/// Wide character version of perror(). void wperror(const wchar_t *s); -/** - Async-safe version of perror(). -*/ +/// Async-safe version of perror(). void safe_perror(const char *message); -/** - Async-safe version of strerror(). -*/ +/// Async-safe version of strerror(). const char *safe_strerror(int err); -// Wide character version of getcwd(). +/// Wide character version of getcwd(). const wcstring wgetcwd(); -/** - Wide character version of chdir() -*/ +/// Wide character version of chdir(). int wchdir(const wcstring &dir); -/** - Wide character version of realpath function. Just like the GNU - version of realpath, wrealpath will accept 0 as the value for the - second argument, in which case the result will be allocated using - malloc, and must be free'd by the user. -*/ +/// Wide character version of realpath function. Just like the GNU version of realpath, wrealpath +/// will accept 0 as the value for the second argument, in which case the result will be allocated +/// using malloc, and must be free'd by the user. wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path); -/** - Wide character version of readdir() -*/ +/// Wide character version of readdir(). bool wreaddir(DIR *dir, std::wstring &out_name); -bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir); - -/** - Like wreaddir, but skip items that are known to not be directories. - If this requires a stat (i.e. the file is a symlink), then return it. - Note that this does not guarantee that everything returned is a directory, - it's just an optimization for cases where we would check for directories anyways. -*/ +bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, + bool *out_is_dir); + +/// Like wreaddir, but skip items that are known to not be directories. If this requires a stat +/// (i.e. the file is a symlink), then return it. Note that this does not guarantee that everything +/// returned is a directory, it's just an optimization for cases where we would check for +/// directories anyways. bool wreaddir_for_dirs(DIR *dir, wcstring *out_name); -/** - Wide character version of dirname() -*/ +/// Wide character version of dirname(). std::wstring wdirname(const std::wstring &path); -/** - Wide character version of basename() -*/ +/// Wide character version of basename(). std::wstring wbasename(const std::wstring &path); -/** - Wide character wrapper around the gettext function. For historic - reasons, unlike the real gettext function, wgettext takes care of - setting the correct domain, etc. using the textdomain and - bindtextdomain functions. This should probably be moved out of - wgettext, so that wgettext will be nothing more than a wrapper - around gettext, like all other functions in this file. -*/ +/// Wide character wrapper around the gettext function. For historic reasons, unlike the real +/// gettext function, wgettext takes care of setting the correct domain, etc. using the textdomain +/// and bindtextdomain functions. This should probably be moved out of wgettext, so that wgettext +/// will be nothing more than a wrapper around gettext, like all other functions in this file. const wchar_t *wgettext(const wchar_t *in); -/** - Wide character version of mkdir -*/ +/// Wide character version of mkdir. int wmkdir(const wcstring &dir, int mode); -/** - Wide character version of rename -*/ +/// Wide character version of rename. int wrename(const wcstring &oldName, const wcstring &newName); -/** Like wcstol(), but fails on a value outside the range of an int */ -int fish_wcstoi(const wchar_t *str, wchar_t ** endptr, int base); +/// Like wcstol(), but fails on a value outside the range of an int. +int fish_wcstoi(const wchar_t *str, wchar_t **endptr, int base); -/** Class for representing a file's inode. We use this to detect and avoid symlink loops, among other things. While an inode / dev pair is sufficient to distinguish co-existing files, Linux seems to aggressively re-use inodes, so it cannot determine if a file has been deleted (ABA problem). Therefore we include richer information. */ -struct file_id_t -{ +/// Class for representing a file's inode. We use this to detect and avoid symlink loops, among +/// other things. While an inode / dev pair is sufficient to distinguish co-existing files, Linux +/// seems to aggressively re-use inodes, so it cannot determine if a file has been deleted (ABA +/// problem). Therefore we include richer information. +struct file_id_t { dev_t device; ino_t inode; uint64_t size; @@ -143,16 +107,16 @@ struct file_id_t long change_nanoseconds; time_t mod_seconds; long mod_nanoseconds; - + bool operator==(const file_id_t &rhs) const; bool operator!=(const file_id_t &rhs) const; - - // Used to permit these as keys in std::map + + // Used to permit these as keys in std::map. bool operator<(const file_id_t &rhs) const; - + static file_id_t file_id_from_stat(const struct stat *buf); - - private: + + private: int compare_file_id(const file_id_t &rhs) const; }; @@ -161,5 +125,4 @@ file_id_t file_id_for_path(const wcstring &path); extern const file_id_t kInvalidFileID; - #endif -- cgit v1.2.3