This commit is contained in:
2025-07-23 13:40:29 -04:00
parent f62cecbfd3
commit 9e94f5ce44
7 changed files with 255 additions and 202 deletions

BIN
c-out/bin/yait Executable file

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,4 @@
PREFIX=/usr/bin/
CFLAGS=-Wall -Wextra -O2
CFLAGS=-Wall -Wextra -g
LDFLAGS=
CC=gcc
CC=clang

View File

@@ -4,32 +4,9 @@
make dist-clean
lint_file() {
local output
output=$(clang-tidy "$1" 2>&1 | grep -v -E 'Error while trying to load a compilation database|No compilation database found|fixed-compilation-database:|json-compilation-database:|Running without flags.')
if [[ -n "$output" ]]; then
echo "[LINT] $1:"
echo "$output"
fi
}
whitespace_cleanup() {
sed -i 's/[ \t]*$//' "$1"
awk 'BEGIN{ORS=""} {print $0 "\n"} END{}' "$1" > "$1.tmp" && mv "$1.tmp" "$1"
}
comment_check() {
if grep -n -E 'TODO|FIXME' "$1"; then
echo "[WARN] $1 contains TODO/FIXME comments."
fi
}
process_file() {
clang-format -i "$1"
tools/check_header_footer "$1"
lint_file "$1"
whitespace_cleanup "$1"
comment_check "$1"
}
if [[ $# -gt 0 ]]; then

44
tools/check_header Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/env bash
# Usage: ./check_header
if pwd | grep -q tools; then
cd ..
fi
files=$(find yait \( -name '*.c' -o -name '*.h' \))
files+=" "
files+=$(find core \( -name '*.c' -o -name '*.h' \))
files+=" $(find . -maxdepth 1 -type f)"
ignore="README COPYING .clang-format config.mak"
if [ -z "$files" ]; then
echo "No files found"
exit 0
fi
missing=""
for file in $files; do
if echo "$ignore" | grep -qw "$(basename "$file")"; then
continue
fi
echo -ne "$file... "
if grep -q "Copyright (C)" "$file"; then
echo -e "\033[1;32mOK\033[0m"
else
echo -e "\033[0;31mFAIL\033[0m"
missing+="$file "
fi
done
if [ "$missing" = "" ]; then
echo -e "\033[1;32mAll checks pass.\033[0m"
else
echo -e "\033[0;31mThe follwing files are missing copyright information.\033[0m"
fi
for file in $missing; do
echo " - $file"
done

View File

@@ -1,71 +0,0 @@
#!/bin/bash
# Usage: ./ensure_header_footer.sh filename
FILE="$1"
HEADER=$(cat <<'EOF'
// Copyright (C) 2025 vx_clutch ( owestness@gmail.com )
// See end of file for extended copyright information.
EOF
)
HEADER+=$'\n'
FOOTER=$(cat <<'EOF'
/* yait is yet another init tool.
* Copyright (C) 2025 vx-clutch
*
* This file is part of yait.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions, and the following disclaimer in the documentation or
* other materials provided with the distribution.
* 3. Neither the name of vx-clutch nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
EOF
)
if [[ -z "$FILE" ]]; then
echo "Usage: $0 <filename>"
exit 1
fi
if [[ ! -f "$FILE" ]]; then
echo "Error: File '$FILE' does not exist."
exit 1
fi
TMP_FILE="$(mktemp)"
read -r FIRST_LINE < "$FILE"
if [[ "$FIRST_LINE" != "$HEADER" ]]; then
echo "$HEADER" > "$TMP_FILE"
cat "$FILE" >> "$TMP_FILE"
mv "$TMP_FILE" "$FILE"
else
rm "$TMP_FILE"
fi
if [[ "$(tail -n 1 "$FILE")" != "$FOOTER" ]]; then
echo "$FOOTER" >> "$FILE"
fi
echo "Checked '$FILE' for header/footer."

View File

@@ -1,9 +1,12 @@
// Usage: yait [OPTION]... [PROJECT] (NAME)
#include "../config.h"
#include "../core/file.h"
#include "../core/print.h"
#include "../core/standard.h"
#include "contents.h"
#include "format.h"
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -12,184 +15,284 @@
#define DEBUG
#define DEFAULT_USER_NAME "unknown"
#define DEFAULT_PROJECT_NAME "Project"
#define DEFAULT_LICENSE BSD3
#define DEFAULT_GIT_INIT true
#define DEFAULT_CLANG_FORMAT true
#define DEFAULT_USER_NAME "unknown"
#define DEFAULT_PROJECT_NAME "Project"
#define DEFAULT_LICENSE BSD3
#define DEFAULT_GIT_INIT true
#define DEFAULT_CLANG_FORMAT true
#define print_option(option, description) \
printf(" %-20s %-20s\n", option, description)
#define print_option(option, description) \
printf (" %-20s %-20s\n", option, description)
#define on_error(msg, code) \
if (err) \
{ \
printfn (msg ": %s", strerror (err)); \
return code; \
}
int create_license_if_needed(format_t);
int create_makefile(format_t);
int create_project(format_t);
int get_license_line_and_create_license(format_t, char**);
int maybe_apply_clang_format(format_t);
int sanitize(format_t *);
int setup_git_and_permissions(format_t);
#ifdef DEBUG
#define debug(fmt, ...) \
fprintf (stderr, "\e[0;32m[%8d]\e[0;33m " fmt "\e[0m\n", __LINE__, \
##__VA_ARGS__)
#define debugc(fmt, ...) \
fprintf (stderr, "\e[0;32m[%8d]\e[0;33m " fmt "...\e[0m", __LINE__, \
##__VA_ARGS__)
#define done fprintf(stderr, "done.\n")
#else
#define debug(fmt, ...)
#define debugc(fmt, ...)
#define done
#endif
// int create_license_if_needed (format_t);
// int get_license_line_and_create_license (format_t, char **);
int create_configure ();
int create_makefile (format_t);
int create_project (format_t);
int generate_source_files (format_t);
int maybe_apply_clang_format (format_t);
int reset_path_ ();
int sanitize (format_t *);
int setup_git (format_t);
int depth;
void
usage(int status)
usage (int status)
{
if (status != 0)
{
fprintf(stderr, "Try 'yait --help' for more information.\n");
fprintf (stderr, "Try 'yait --help' for more information.\n");
return;
}
printf("Usage: yait [OPTION]... [PROJECT] (NAME)\n");
printf("Creates a C project with opinionated defaults.\n");
printf("When only given the first argument it will detect your name.\n\n");
printf("Mandatory arguments to long options are mandatory for short options too\n");
print_option("-l, --license=NAME", "Set license (gpl, mit, bsd) [default: gpl]");
print_option("--use-cpp", "Uses the CPP language instead of C");
print_option("--git", "Initialize git repository");
print_option("--GNU", "Adds standard GNU argument parsing to your project");
printf(" --help display this help text and exit\n");
printf(" --version output version information and exit\n");
printf ("Usage: yait [OPTION]... [PROJECT] (NAME)\n");
printf ("Creates a C project with opinionated defaults.\n");
printf ("When only given the first argument it will detect your name.\n\n");
printf ("Mandatory arguments to long options are mandatory for short "
"options too\n");
print_option ("-l, --license=NAME",
"Set license (gpl, mit, bsd) [default: bsd]");
print_option ("--use-cpp", "Uses the CPP language instead of C");
print_option ("--git", "Initialize git repository");
print_option ("--GNU", "Adds standard GNU argument parsing to your project");
printf (" --help display this help text and exit\n");
printf (" --version output version information and exit\n");
}
int
main(int argc, char **argv)
main (int argc, char **argv)
{
if (argc < 2)
{
printfn("error: not enough arguments.");
printfn ("error: not enough arguments.");
return 1;
}
int status = initialize_main(&argc, &argv);
status = parse_standard_options(usage, argc, argv);
int status = initialize_main (&argc, &argv);
status = parse_standard_options (usage, argc, argv);
if (status && status != HELP_REQUESTED)
{
printfn("error: %s", strerror(status));
printfn ("error: %s", strerror (status));
return status;
}
format_t conf;
conf.project = argv[0];
conf.name = (argc > 2) ? argv[1] : NULL;
format_t conf = { 0 };
conf.project = argv[0]; // fix: project name is argv[1]
conf.name = (argc > 1) ? argv[1] : NULL; // fix: name is optional, at argv[2]
if (!conf.name)
{
struct passwd *pw = getpwuid(getuid());
struct passwd *pw = getpwuid (getuid ());
conf.name = (pw && pw->pw_name) ? pw->pw_name : DEFAULT_USER_NAME;
}
conf.flag.git = DEFAULT_GIT_INIT;
conf.flag.clang_format = DEFAULT_CLANG_FORMAT;
conf.licence = DEFAULT_LICENSE;
#ifdef DEBUG
system(strcat("rm -rf ", conf.project));
#endif
return create_project(conf);
return create_project (conf);
}
int
create_project(format_t fmt)
create_project (format_t fmt)
{
int err;
err = create_and_enter_directory(fmt.project);
if (err)
{
printfn("failed to create or enter directory: %s", strerror(err));
return err;
}
debugc ("sanitize... ");
err = sanitize (&fmt);
on_error ("failed to sanitize format", err);
done;
err = sanitize(&fmt);
if (err)
{
printfn("failed to sanitize format: %s", strerror(err));
return err;
}
debugc ("take %s", fmt.project);
err = create_and_enter_directory (fmt.project);
on_error ("failed to create or enter directory", err);
done;
depth = 0;
err = create_license_if_needed(fmt);
if (err)
{
printfn("failed to create license: %s", strerror(err));
return err;
}
// debug ("create licenseing");
// err = create_license_if_needed (fmt);
// on_error ("failed to create license", err);
err = create_makefile(fmt);
if (err)
{
printfn("failed to create Makefile: %s", strerror(err));
return err;
}
debugc ("create makefile");
err = create_makefile (fmt);
on_error ("failed to create Makefile", err);
done;
err = setup_git_and_permissions(fmt);
debug ("setup git");
err = setup_git (fmt);
if (err)
{
printfn("warning: git initialization failed: %s", strerror(err));
// continue even if git fails
}
printfn ("warning: git initialization failed: %s", strerror (err));
err = maybe_apply_clang_format(fmt);
debug ("create .clang-format");
err = maybe_apply_clang_format (fmt);
if (err)
{
printfn("warning: clang-format setup failed: %s", strerror(err));
}
printfn ("warning: clang-format setup failed: %s", strerror (err));
return 0;
}
#define reset_path reset_path_ ()
int
sanitize(format_t *fmt)
reset_path_ ()
{
while (depth != 0)
{
if (chdir ("..") != 0)
return errno;
else
depth--;
}
return 0;
}
int
sanitize (format_t *fmt)
{
if (!fmt->name)
fmt->name = DEFAULT_USER_NAME;
return 0;
}
int
create_license_if_needed(format_t fmt)
{
char *license_line = NULL;
return get_license_line_and_create_license(fmt, &license_line);
}
// int
// create_license_if_needed (format_t fmt)
// {
// char *license_line = NULL;
// return get_license_line_and_create_license (fmt, &license_line);
// }
//
// int
// get_license_line_and_create_license (format_t fmt, char
// **license_line_buffer)
// {
// switch (fmt.licence)
// {
// case BSD3:
// *license_line_buffer = "License BSD-3-Clause: BSD-3-Clause "
// "<https://opensource.org/license/bsd-3-clause/>";
// return create_file_with_content ("COPYING", bsd3_license_template,
// YEAR,
// fmt.name);
//
// case GPLv3:
// default:
// *license_line_buffer = "License GPLv3: GNU GPL version 3 "
// "<https://www.gnu.org/licenses/gpl-3.0.html>";
// return create_file_with_content ("COPYING", gplv3_license_template,
// YEAR,
// fmt.name);
// }
// }
int
get_license_line_and_create_license(format_t fmt, char **license_line_buffer)
{
switch (fmt.licence)
{
case BSD3:
*license_line_buffer =
"License BSD-3-Clause: BSD-3-Clause <https://opensource.org/licence/bsd-3-clause>";
return create_file_with_content("COPYING", bsd3_license_template, YEAR, fmt.name);
case GPLv3:
default:
*license_line_buffer = "License GPLv3: GNU GPL version 3 <https://www.gnu.org/licenses/gpl-3.0.html>";
return create_file_with_content("COPYING", gplv3_license_template, YEAR, fmt.name);
}
}
int
maybe_apply_clang_format(format_t fmt)
maybe_apply_clang_format (format_t fmt)
{
if (!fmt.flag.clang_format)
return 0;
const char *clang_fmt = "BasedOnStyle: LLVM\nIndentWidth: 2\nUseTab: Never\n";
return create_file_with_content(".clang-format", clang_fmt, 0, NULL);
reset_path;
char *clang_fmt = "BasedOnStyle: LLVM\nIndentWidth: 2\nUseTab: Never\n";
return create_file_with_content (".clang-format", clang_fmt, 0, NULL);
}
int
setup_git_and_permissions(format_t fmt)
setup_git (format_t fmt)
{
if (!fmt.flag.git)
return 0;
int err = system("git init --quiet");
reset_path;
int err = system ("git init --quiet");
if (err)
{
printfn("failed on git initialize: %s", strerror(err));
}
printfn ("failed on git initialize: %s", strerror (err));
return err;
}
int
create_makefile (format_t fmt)
{
char *makefile_name = strdup (fmt.project);
if (!makefile_name)
{
printfn ("fatal: out of memory");
return 1;
}
for (char *p = makefile_name; *p; ++p)
if (*p >= 'a' && *p <= 'z')
*p -= 32;
reset_path;
create_file_with_content ("Makefile", makefile_template, makefile_name,
makefile_name, makefile_name, makefile_name,
makefile_name, makefile_name, fmt.project,
makefile_name);
free (makefile_name);
return 0;
}
int
create_configure ()
{
reset_path;
create_file_with_content ("configure", configure_template);
int err = system ("chmod +x configure");
if (err)
printfn ("error: %s", strerror (err));
return err;
}
int
generate_source_files (format_t fmt)
{
int err;
debug ("take %s/%s", fmt.project, fmt.project);
err = create_and_enter_directory (fmt.project);
on_error ("failed to create or enter directory", err);
if (fmt.flag.GNU)
{
debug ("GNU flag source branch");
create_file_with_content ("main.c", main_c_gnu_template);
goto atexit_clean;
}
debug ("default sourcebranch");
create_file_with_content ("main.c", main_c_template);
atexit_clean:
reset_path;
return 0;
}