From 6ed8f5151719fbc14ec0ac6d28a346d1f74cf2ca Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Fri, 2 Jan 2026 22:42:31 +0100 Subject: Initial commit --- .clang-format | 315 ++++++++++++++++ .clang-tidy | 2 + .dir-locals.el | 13 + .gitignore | 1 + meson.build | 373 +++++++++++++++++++ src/args.cc | 389 ++++++++++++++++++++ src/args.hh | 64 ++++ src/cfg.cc | 199 ++++++++++ src/cfg.hh | 45 +++ src/check.hh | 39 ++ src/colour.cc | 15 + src/colour.hh | 20 + src/config.h.in | 6 + src/image.cc | 15 + src/image.hh | 62 ++++ src/image_loader.hh | 15 + src/image_processor.cc | 947 ++++++++++++++++++++++++++++++++++++++++++++++++ src/image_processor.hh | 29 ++ src/io.cc | 358 ++++++++++++++++++ src/io.hh | 109 ++++++ src/line.cc | 127 +++++++ src/line.hh | 37 ++ src/logger.cc | 125 +++++++ src/logger.hh | 41 +++ src/main.cc | 47 +++ src/paths.cc | 100 +++++ src/paths.hh | 20 + src/size.hh | 30 ++ src/spawner.cc | 236 ++++++++++++ src/spawner.hh | 49 +++ src/str.cc | 94 +++++ src/str.hh | 34 ++ src/u.hh | 21 ++ src/u8.hh | 196 ++++++++++ src/unique_fd.cc | 9 + src/unique_fd.hh | 36 ++ src/xpm/.clang-tidy | 2 + src/xpm/color.c | 882 ++++++++++++++++++++++++++++++++++++++++++++ src/xpm/dix-config.h | 1 + src/xpm/dix/dix_priv.h | 14 + src/xpm/include/dix.h | 14 + subprojects/.gitignore | 3 + subprojects/gtest.wrap | 16 + test/args.cc | 412 +++++++++++++++++++++ test/cfg.cc | 190 ++++++++++ test/data/test.jpeg | Bin 0 -> 734 bytes test/data/test.png | Bin 0 -> 171 bytes test/data/test.svg | 62 ++++ test/data/test.xpm | 17 + test/image_processor.cc | 251 +++++++++++++ test/io.cc | 274 ++++++++++++++ test/io_test_helper.cc | 165 +++++++++ test/io_test_helper.hh | 27 ++ test/line.cc | 182 ++++++++++ test/paths.cc | 63 ++++ test/str.cc | 164 +++++++++ test/testdir.cc | 37 ++ test/testdir.hh | 22 ++ test/testenv.cc | 44 +++ test/testenv.hh | 22 ++ test/u8.cc | 276 ++++++++++++++ 61 files changed, 7358 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .dir-locals.el create mode 100644 .gitignore create mode 100644 meson.build create mode 100644 src/args.cc create mode 100644 src/args.hh create mode 100644 src/cfg.cc create mode 100644 src/cfg.hh create mode 100644 src/check.hh create mode 100644 src/colour.cc create mode 100644 src/colour.hh create mode 100644 src/config.h.in create mode 100644 src/image.cc create mode 100644 src/image.hh create mode 100644 src/image_loader.hh create mode 100644 src/image_processor.cc create mode 100644 src/image_processor.hh create mode 100644 src/io.cc create mode 100644 src/io.hh create mode 100644 src/line.cc create mode 100644 src/line.hh create mode 100644 src/logger.cc create mode 100644 src/logger.hh create mode 100644 src/main.cc create mode 100644 src/paths.cc create mode 100644 src/paths.hh create mode 100644 src/size.hh create mode 100644 src/spawner.cc create mode 100644 src/spawner.hh create mode 100644 src/str.cc create mode 100644 src/str.hh create mode 100644 src/u.hh create mode 100644 src/u8.hh create mode 100644 src/unique_fd.cc create mode 100644 src/unique_fd.hh create mode 100644 src/xpm/.clang-tidy create mode 100644 src/xpm/color.c create mode 100644 src/xpm/dix-config.h create mode 100644 src/xpm/dix/dix_priv.h create mode 100644 src/xpm/include/dix.h create mode 100644 subprojects/.gitignore create mode 100644 subprojects/gtest.wrap create mode 100644 test/args.cc create mode 100644 test/cfg.cc create mode 100644 test/data/test.jpeg create mode 100644 test/data/test.png create mode 100644 test/data/test.svg create mode 100644 test/data/test.xpm create mode 100644 test/image_processor.cc create mode 100644 test/io.cc create mode 100644 test/io_test_helper.cc create mode 100644 test/io_test_helper.hh create mode 100644 test/line.cc create mode 100644 test/paths.cc create mode 100644 test/str.cc create mode 100644 test/testdir.cc create mode 100644 test/testdir.hh create mode 100644 test/testenv.cc create mode 100644 test/testenv.hh create mode 100644 test/u8.cc diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..aa4a4dc --- /dev/null +++ b/.clang-format @@ -0,0 +1,315 @@ +--- +Language: Cpp +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: true + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AllowShortNamespacesOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: BinPack +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BracedInitializerIndentWidth: 2 +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakBinaryOperations: Never +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: Yes +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](lzma|z|test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: false + AtStartOfFile: true +KeepFormFeed: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakBeforeMemberAccess: 150 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +PPIndentWidth: 1 +QualifierAlignment: Leave +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: IndentOnly +RemoveBracesLLVM: false +RemoveEmptyLinesInUnwrappedLines: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +WrapNamespaceBodyWithEmptyLines: Leave +... + diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..27aad9a --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,2 @@ +--- +Checks: 'bugprone-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-bugprone-easily-swappable-parameters,-bugprone-unchecked-optional-access,-misc-non-private-member-variables-in-classes,-misc-const-correctness,-misc-no-recursion,-modernize-avoid-c-arrays,-modernize-use-trailing-return-type,-readability-magic-numbers,-readability-identifier-length,-readability-braces-around-statements,-readability-function-cognitive-complexity,-readability-redundant-inline-specifier,-readability-implicit-bool-conversion,-readability-qualified-auto,-misc-header-include-cycle' diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..36206e4 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,13 @@ +;;; Directory Local Variables -*- no-byte-compile: t; -*- +;;; For more information see (info "(emacs) Directory Variables") + +((c++-mode . ((eval + . + (let ((project-path + (locate-dominating-file default-directory ".dir-locals.el"))) + (setq-local flycheck-clangcheck-build-path + (concat project-path "build")) + (setq-local flycheck-clang-language-standard "c++23") + (setq-local flycheck-clang-definitions '("HAVE_CONFIG_H")) + (setq-local flycheck-clang-include-path '("../src" "../build" +"/usr/include/librsvg-2.0" "/usr/include/gdk-pixbuf-2.0" "/usr/include/glycin-2" "/usr/include/cairo" "/usr/include/libxml2" "/usr/include/pango-1.0" "/usr/include/libmount" "/usr/include/blkid" "/usr/include/fribidi" "/usr/include/pixman-1" "/usr/include/harfbuzz" "/usr/include/freetype2" "/usr/include/libpng16" "/usr/include/glib-2.0" "/usr/lib/glib-2.0/include" "/usr/include/sysprof-6"))))))) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..7edf83c --- /dev/null +++ b/meson.build @@ -0,0 +1,373 @@ +project( + 'sawmill', + 'cpp', + 'c', + version : '0.10', + meson_version : '>= 1.3.0', + default_options : [ + 'warning_level=3', + 'cpp_std=c++26', + 'cpp_eh=none', + 'cpp_rtti=false', + 'default_library=static', + ], +) + +c_flags = [] +c_optional_flags = [] +cpp_flags = [] +if get_option('buildtype') == 'release' + # If asserts are disabled parameters and variables used for only that + # end up causing warnings + c_optional_flags += ['-Wno-unused-parameter', '-Wno-unused-variable', + '-Wno-unused-but-set-variable'] + c_flags += '-DNDEBUG' + cpp_flags += '-DNDEBUG' +endif +cpp = meson.get_compiler('cpp') +foreach flag : c_optional_flags + if cpp.has_argument(flag) + cpp_flags += flag + endif +endforeach +c = meson.get_compiler('c') +foreach flag : c_optional_flags + if c.has_argument(flag) + c_flags += flag + endif +endforeach +add_project_arguments([cpp_flags], language: 'cpp') +add_project_arguments([c_flags], language: 'c') + +jpeg = dependency('libjpeg', version: '>= 2.1.5', required: false) +png = dependency('libpng', version: '>= 1.6.0', required: true) +rsvg = dependency('librsvg-2.0', version: '>= 2.61.0', required: false) +xpm = dependency('xpm', version: '>= 3.5.0', required: false) + +image_deps = [png] + +if jpeg.found() + image_deps += jpeg +endif + +if rsvg.found() + image_deps += rsvg +endif + +if xpm.found() + image_deps += xpm +endif + +conf_data = configuration_data() +conf_data.set('version', meson.project_version()) +conf_data.set10('have_jpeg', jpeg.found()) +conf_data.set10('have_png', png.found()) +conf_data.set10('have_rsvg', rsvg.found()) +conf_data.set10('have_xpm', xpm.found()) +configure_file(input: 'src/config.h.in', + output: 'config.h', + configuration : conf_data) + +inc = include_directories('src') + +args_lib = library( + 'args', + sources: [ + 'src/args.cc', + 'src/args.hh', + ], + include_directories: inc, +) +args_dep = declare_dependency(link_with: args_lib) + +io_lib = library( + 'io', + sources: [ + 'src/check.hh', + 'src/line.cc', + 'src/line.hh', + 'src/io.cc', + 'src/io.hh', + 'src/unique_fd.cc', + 'src/unique_fd.hh', + ], + include_directories: inc, +) +io_dep = declare_dependency(link_with: io_lib) + +str_lib = library( + 'str', + sources: [ + 'src/str.cc', + 'src/str.hh', + ], + include_directories: inc, +) +str_dep = declare_dependency(link_with: str_lib) + +paths_lib = library( + 'paths', + sources: [ + 'src/paths.cc', + 'src/paths.hh', + ], + include_directories: inc, + dependencies: [str_dep], +) +paths_dep = declare_dependency( + link_with: paths_lib, + dependencies: [str_dep], +) + +cfg_lib = library( + 'cfg', + sources: [ + 'src/cfg.cc', + 'src/cfg.hh', + ], + include_directories: inc, + dependencies: [io_dep, paths_dep, str_dep], +) +cfg_dep = declare_dependency( + link_with: cfg_lib, + dependencies: [io_dep, paths_dep, str_dep], +) + +logger_lib = library( + 'logger', + sources: [ + 'src/logger.cc', + 'src/logger.hh', + ], + include_directories: inc, +) +logger_dep = declare_dependency(link_with: logger_lib) + +if xpm.found() + xpm_color_inc = include_directories('src/xpm') + xpm_color_lib = library( + 'xpm_color', + sources: [ + 'src/xpm/dix-config.h', + 'src/xpm/dix/dix_priv.h', + 'src/xpm/include/dix.h', + 'src/xpm/color.c', + ], + c_args: ['-Wno-sign-compare', '-Wno-unused-parameter'], + include_directories: xpm_color_inc, + dependencies: [xpm], + ) + xpm_color_dep = declare_dependency( + link_with: xpm_color_lib, + dependencies: [xpm], + ) + + image_deps += xpm_color_dep +endif + +image_processor_lib = library( + 'image_processor', + sources: [ + 'src/colour.cc', + 'src/colour.hh', + 'src/image.cc', + 'src/image.hh', + 'src/image_processor.cc', + 'src/image_processor.hh', + 'src/size.hh', + ], + include_directories: inc, + dependencies : image_deps, +) +image_processor_dep = declare_dependency( + link_with: image_processor_lib, + dependencies: image_deps, +) + +spawner_lib = library( + 'spawner', + sources: [ + 'src/spawner.cc', + 'src/spawner.hh', + ], + include_directories: inc, + dependencies : [ + io_dep, + ], +) +spawner_dep = declare_dependency( + link_with: spawner_lib, + dependencies: [ + io_dep, + ], +) + +sawmill = executable( + 'sawmill', + sources: [ + 'src/main.cc', + ], + include_directories: inc, + install : true, + dependencies : [ + args_dep, + cfg_dep, + image_processor_dep, + spawner_dep, + ], +) + +gtest_main_dep = dependency('gtest_main', fallback : ['gtest', 'gtest_main_dep']) +gmock_dep = dependency('gmock', fallback : ['gtest', 'gmock_dep']) + +test_dependencies = [ + gtest_main_dep, +] + +test('args', executable( + 'test_args', + sources: ['test/args.cc'], + include_directories: inc, + dependencies: [ + args_dep, + test_dependencies, + ], +)) + +io_test_helper_lib = library( + 'io_test_helper', + sources: [ + 'test/io_test_helper.cc', + 'test/io_test_helper.hh', + ], + include_directories: inc, + dependencies: io_dep, +) +io_test_helper_dep = declare_dependency( + link_with: io_test_helper_lib, + dependencies: io_dep, +) + +testdir_lib = library( + 'testdir', + sources: [ + 'test/testdir.cc', + 'test/testdir.hh', + ], + include_directories: inc, + dependencies: test_dependencies, +) +testdir_dep = declare_dependency( + link_with: testdir_lib, + dependencies: test_dependencies, +) + +testenv_lib = library( + 'testenv', + sources: [ + 'test/testenv.cc', + 'test/testenv.hh', + ], + include_directories: inc, + dependencies: test_dependencies, +) +testenv_dep = declare_dependency( + link_with: testenv_lib, + dependencies: test_dependencies, +) + +test('line', executable( + 'test_line', + sources: ['test/line.cc'], + include_directories: inc, + dependencies: [ + io_dep, + io_test_helper_dep, + test_dependencies, + ], +)) + +test('str', executable( + 'test_str', + sources: ['test/str.cc'], + include_directories: inc, + dependencies: [ + str_dep, + test_dependencies, + ], +)) + +test('io', executable( + 'test_io', + sources: ['test/io.cc'], + include_directories: inc, + dependencies: [ + io_dep, + io_test_helper_dep, + test_dependencies, + ], +)) + +test('paths', executable( + 'test_paths', + sources: ['test/paths.cc'], + include_directories: inc, + dependencies : [ + paths_dep, + test_dependencies, + testenv_dep, + ], +)) + +test('cfg', executable( + 'test_cfg', + sources: ['test/cfg.cc'], + include_directories: inc, + dependencies : [ + cfg_dep, + test_dependencies, + testdir_dep, + testenv_dep, + ], +)) + +test('u8', executable( + 'test_u8', + sources: [ + 'src/u.hh', + 'src/u8.hh', + 'test/u8.cc', + ], + include_directories: inc, + dependencies : [ + test_dependencies, + ], +)) + +test('image_processor', executable( + 'test_image_processor', + sources: ['test/image_processor.cc'], + cpp_args: ['-DTESTDIR="@0@/test/data"'.format(meson.current_source_dir())], + include_directories: inc, + dependencies : [ + gmock_dep, + image_processor_dep, + spawner_dep, + test_dependencies, + ], +)) + +run_clang_tidy = find_program('run-clang-tidy', required: false) + +if run_clang_tidy.found() + # The clang-tidy target generated by meson misses most of the + # source files, so create our own. + run_target( + 'clang-tidy', + command: [ + run_clang_tidy, + '-quiet', + '-use-color', + ], + ) +endif diff --git a/src/args.cc b/src/args.cc new file mode 100644 index 0000000..1794941 --- /dev/null +++ b/src/args.cc @@ -0,0 +1,389 @@ +#include "args.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +std::string kEmpty; + +class OptionImpl : public Args::OptionArgument { + public: + enum Type : uint8_t { + NoArgument, + RequiredArgument, + OptionalArgument, + }; + + OptionImpl(Type type, char shortname, std::string longname, std::string arg, + std::string help) + : type(type), + shortname(shortname), + longname(std::move(longname)), + arg(std::move(arg)), + help(std::move(help)) {} + + const Type type; + const char shortname; + const std::string longname; + const std::string arg; + const std::string help; + + [[nodiscard]] bool is_set() const override { return is_set_; } + + [[nodiscard]] bool has_argument() const override { + return value_.has_value(); + } + + [[nodiscard]] const std::string& argument() const override { + if (value_.has_value()) + return value_.value(); + assert(false); + return kEmpty; + } + + void clear() { + is_set_ = false; + value_.reset(); + } + + void set_argument(std::string value) { + assert(type == Type::RequiredArgument || type == Type::OptionalArgument); + is_set_ = true; + value_ = std::move(value); + } + + void set_no_argument() { + assert(type == Type::NoArgument || type == Type::OptionalArgument); + is_set_ = true; + value_.reset(); + } + + private: + bool is_set_{false}; + std::optional value_; +}; + +class ArgsImpl : public Args { + public: + explicit ArgsImpl(std::string prgname) : prgname_(std::move(prgname)) {} + + std::shared_ptr