+/* -*-c-*-
+ *
+ * Arrange to have code executed when a function ends
+ *
+ * (c) 2023 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the `Finally' package.
+ *
+ * Finally 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.
+ *
+ * Finally 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 Finally. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef FINALLY_H
+#define FINALLY_H
+
+/*----- Compatibility machinery -------------------------------------------*/
+
+/* Some preliminary hacks for detecting compiler versions. */
+#ifdef __GNUC__
+# define FINALLY__GCC_P(maj, min) \
+ (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))
+#else
+# define FINALLY__GCC_P(maj, min) 0
+#endif
+
+#ifdef __clang__
+# define FINALLY__CLANG_P(maj, min) \
+ (__clang_major__ > (maj) || (__clang_major__ == (maj) && \
+ __clang_minor__ >= (min)))
+#else
+# define FINALLY__CLANG_P(maj, min) 0
+#endif
+
+/* If configuration machinery hasn't determined a flavour, then we'll take a
+ * rough guess based on compiler versions.
+ */
+#if !defined(FINALLY_CONFIG_FLAVOUR) && \
+ !defined(__clang__) && FINALLY__GCC_P(3, 3)
+# define FINALLY_CONFIG_FLAVOUR GCC_NESTED_FUNCTIONS
+#endif
+
+#if !defined(FINALLY_CONFIG_FLAVOUR)
+# define FINALLY_CONFIG_FLAVOUR NIL
+#endif
+
+/*----- Macros provided ---------------------------------------------------*/
+
+/* Before we start, a note about compatibility. We're using pretty esoteric
+ * compiler features here, and not all compilers support them. I'm most
+ * interested in GCC, which will work fine. This isn't going to work for
+ * other compilers, but lots of them try to impersonate GCC, and it's just
+ * not worth the effort to try to see through their lies.
+ *
+ * So the rules are: if you include this header file, you've either already
+ * made an effort to check that it's likely to work (e.g., by using the
+ * provided Autoconf macro), or you're willing to put up with whatever
+ * wreckage you end up with if the compiler doesn't actually like what we're
+ * saying here.
+ */
+
+/* And a utility for pasting tokens after macro expansion. */
+#define FINALLY__GLUE(x, y) FINALLY__DOGLUE(x, y)
+#define FINALLY__DOGLUE(x, y) x##y
+
+/* Now, we need a way to make a temporary name which isn't likely to conflict
+ * with anything else.
+ */
+#define FINALLY__TMP(name) FINALLY__GLUE(_finally__##name##__, __LINE__)
+
+/* And some other tricks which we may or may not need. */
+#if defined(__cplusplus) && __cplusplus >= 201703
+# define FINALLY__IGNORABLE fucksocks
+#elif FINALLY__GCC_P(2, 5) || FINALLY__CLANG_P(3, 3)
+# define FINALLY__IGNORABLE __attribute__((__unused__))
+#endif
+#ifndef FINALLY__IGNORABLE
+# define FINALLY__IGNORABLE
+#endif
+
+/* Flavour selection machinery. */
+#define FINALLY__FLAVOUR_NIL -1
+#define FINALLY__FLAVOUR_GCC_NESTED_FUNCTIONS 1
+
+#define FINALLY__SELECTED_FLAVOUR \
+ FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR)
+
+/* @FINALLY(code)@
+ * @FINALLY_TAGGED(tag, code)@
+ *
+ * This macro may be placed anywhere within a function where a declaration is
+ * acceptable -- so at the head of a block in C89, or anywhere between
+ * statements in C99 or later. It causes @code@ to be executed when control
+ * leaves the enclosing scope. If it's placed at top-level within a
+ * function, for example, then the @code@ is executed when the function
+ * returns.
+ *
+ * There may be multiple @FINALLY@ invocations within a scope; they are
+ * executed in reverse order when control leaves the scope.
+ *
+ * Due to technical limitations, it's forbidden to have two @FINALLY@
+ * invocations on the same source line. This would seem to stymie writing a
+ * macro which expands to two invocations of @FINALLY@, which would be useful
+ * if those invocations were to end up in different scopes. Such a macro can
+ * still be written using @FINALLY_TAGGED@ instead, which takes an additional
+ * @tag@ argument, which may be any identifier: the rule then becomes that a
+ * source line may not contain two invocations of @FINALLY_TAGGED@ bearing
+ * the same @tag@. (It would be possible to overcome this limitation using
+ * the @__COUNTER__@ GCC extension, but I'd prefer not to limit the potential
+ * portability of this feature even further by insisting that something
+ * analogous exist in every supported compiler, and it would do users a
+ * disservice to create portability problems by having different rules on
+ * different compilers.)
+ */
+#if defined(__cplusplus) && __cplusplus >= 201103
+ /* Oooh, we're being compiled by a C++ compiler. There's no need to deploy
+ * nonportable tricks, because we have an insane language that can already
+ * do what we need.
+ */
+
+ namespace finally {
+ template<typename F> class Finally {
+ F fn;
+ public:
+ Finally(F f) : fn{f} { ; }
+ ~Finally() { fn(); }
+ };
+ template<typename F> Finally<F> finally(F &&fn)
+ { return Finally<F>(fn); }
+ }
+
+# define FINALLY_TAGGED(tag, body) \
+ FINALLY__IGNORABLE auto FINALLY__TMP(tag##__var) = \
+ finally::finally([&]{ body })
+
+#elif FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR) == \
+ FINALLY__FLAVOUR_GCC_NESTED_FUNCTIONS
+ /* We're being compiled by GCC, or by something which wants to be mistaken
+ * for GCC at any rate. And GCC has nested functions. So, for each
+ * block, we'll define a dummy variable that we don't care about, and
+ * attach a nested function as its cleanup handler which will execute our
+ * cleanup code.
+ */
+
+# define FINALLY_TAGGED(tag, body) \
+ __extension__ __inline__ \
+ void FINALLY__TMP(tag##__fn) \
+ (const int __attribute__((__unused__)) *_finally__hunoz) \
+ { body } \
+ __attribute__((__unused__, __cleanup__(FINALLY__TMP(tag##__fn)))) \
+ int FINALLY__TMP(tag##__var)
+
+#elif FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR) == \
+ FINALLY__GLUE(FINALLY__FLAVOUR_, NIL)
+ /* We don't have a flavour to support this environment. */
+
+# error "Compiler not supported. This isn't going to work."
+#else
+ /* This case analysis should be exhaustive. */
+
+# error "Internal error: `FINALLY_CONFIG_FLAVOUR' bungled."
+#endif
+
+/* We now have `FINALLY_TAGGED'; defining `FINALLY' is easy. The TAG here is
+ * guaranteed not conflict with any call on `FINALLY_TAGGED', since TAGs are
+ * required to be identifiers.
+ */
+#define FINALLY(code) FINALLY_TAGGED(0, code)
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#endif