summaryrefslogtreecommitdiffstats
path: root/src/modules/separator/separator.c
blob: 4c5b4bc92c85c513267f8fe2c5d3878d9328b937 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/strutil.h"
#include "common/textModifier.h"
#include "logo/logo.h"
#include "modules/separator/separator.h"

bool ffPrintSeparator(FFSeparatorOptions* options) {
    ffLogoPrintLine();

    if (options->outputColor.length && !instance.config.display.pipe) {
        ffPrintColor(&options->outputColor);
    }

    if (options->times > 0) {
        if (__builtin_expect(options->string.length == 1, 1)) {
            ffPrintCharTimes(options->string.chars[0], options->times);
        } else {
            for (uint32_t i = 0; i < options->times; i++) {
                fputs(options->string.chars, stdout);
            }
        }
    } else {
        const FFPlatform* platform = &instance.state.platform;

        uint32_t titleLength = 1                                                                                      // @
            + ffUtf8StrWidth(platform->userName.chars, platform->userName.length)                                     // user name
            + (instance.state.titleFqdn ? platform->hostName.length : ffStrbufFirstIndexC(&platform->hostName, '.')); // host name

        if (__builtin_expect(options->string.length == 1, 1)) {
            ffPrintCharTimes(options->string.chars[0], titleLength);
        } else {
            uint32_t wcsLength = ffUtf8StrWidth(options->string.chars, options->string.length);

            int remaining = (int) titleLength;
            // Write the whole separator as often as it fits fully into titleLength
            for (; remaining >= (int) wcsLength; remaining -= (int) wcsLength) {
                ffStrbufWriteTo(&options->string, stdout);
            }

            if (remaining > 0) {
                // Write as much of the separator as needed to fill titleLength
                if (wcsLength != options->string.length) {
                    // Unicode chars
                    const char* ptr = options->string.chars;
                    uint32_t remainBytes = options->string.length;
                    while (remaining > 0 && remainBytes > 0 && *ptr != '\0') {
                        uint8_t charWidth = 0;
                        uint8_t bytes = ffUtf8CharLenWidth(ptr, remainBytes, &charWidth);
                        if (__builtin_expect(bytes == 0, false)) {
                            break;
                        }

                        remaining -= (int) charWidth;
                        ptr += bytes;
                        remainBytes -= bytes;
                    }
                    fwrite(options->string.chars, (size_t) (ptr - options->string.chars), 1, stdout);
                } else {
                    fwrite(options->string.chars, (size_t) remaining, 1, stdout);
                }
            }
        }
    }

    if (options->outputColor.length && !instance.config.display.pipe) {
        fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
    }
    putchar('\n');

    return true;
}

void ffParseSeparatorJsonObject(FFSeparatorOptions* options, yyjson_val* module) {
    yyjson_val *key, *val;
    size_t idx, max;
    yyjson_obj_foreach (module, idx, max, key, val) {
        if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition")) {
            continue;
        }

        if (unsafe_yyjson_equals_str(key, "string")) {
            ffStrbufSetJsonVal(&options->string, val);
            continue;
        }

        if (unsafe_yyjson_equals_str(key, "outputColor")) {
            ffOptionParseColor(yyjson_get_str(val), &options->outputColor);
            continue;
        }

        if (unsafe_yyjson_equals_str(key, "times")) {
            options->times = (uint32_t) yyjson_get_uint(val);
            continue;
        }

        if (unsafe_yyjson_equals_str(key, "length")) {
            ffPrintError(FF_SEPARATOR_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "The option length has been renamed to times.");
            continue;
        }

        ffPrintError(FF_SEPARATOR_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
    }
}

void ffGenerateSeparatorJsonConfig(FFSeparatorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
    yyjson_mut_obj_add_strbuf(doc, module, "string", &options->string);
    yyjson_mut_obj_add_strbuf(doc, module, "outputColor", &options->outputColor);
    yyjson_mut_obj_add_uint(doc, module, "times", options->times);
}

void ffInitSeparatorOptions(FFSeparatorOptions* options) {
    ffStrbufInitStatic(&options->string, "-");
    ffStrbufInit(&options->outputColor);
    options->times = 0;
}

void ffDestroySeparatorOptions(FFSeparatorOptions* options) {
    ffStrbufDestroy(&options->string);
}

FFModuleBaseInfo ffSeparatorModuleInfo = {
    .name = FF_SEPARATOR_MODULE_NAME,
    .description = "Print a separator line",
    .initOptions = (void*) ffInitSeparatorOptions,
    .destroyOptions = (void*) ffDestroySeparatorOptions,
    .parseJsonObject = (void*) ffParseSeparatorJsonObject,
    .printModule = (void*) ffPrintSeparator,
    .generateJsonConfig = (void*) ffGenerateSeparatorJsonConfig,
};