Português | English
- O que é a Xgen?
- Features
- Instalação
- Início Rápido
- Guia de Uso
- Modelo de Dados
- Constraints: Dependências e Exclusões
- Filtragem por Peso
- Macros Auxiliares
- Exemplos Práticos
- API de Referência
- Troubleshooting
- Contribuição
A Xgen é uma biblioteca em C que gera, de forma exaustiva e sem repetições, todas as combinações válidas de argumentos de linha de comandos a partir de grupos de aliases semanticamente equivalentes — respeitando dependências, exclusões e limites de peso definidos pelo utilizador.
É útil sempre que é preciso gerar sistematicamente casos de teste para uma CLI: fuzzing dirigido, testes de regressão de parsers de argumentos, ou simplesmente explorar o espaço de combinações válidas de um programa com muitas flags interdependentes (como o gcc).
Compatibilidade:
- Sistemas Operativos: Linux, Windows
- Arquitecturas: x86, x86_64, ARM, AArch64
- Padrão: C11+
| Categoria | Feature | Detalhes | Status |
|---|---|---|---|
| Core | Geração combinatória | State machine explora k-combinações, permutações e aliases | Completo |
| Constraints | Dependências | AND (DEP) e OR (DEP_OR) |
Completo |
| Constraints | Exclusões | Par-a-par (EXCL) e colectivas (EXCL_OR) |
Completo |
| Filtragem | Peso cumulativo | max_weight filtra combinações por custo/complexidade |
Completo |
| Performance | Memoização | Validação de dependências/exclusões feita ao nível do grupo, não repetida por alias/permutação | Completo |
| Modos | Geração | GEN_MODE_LEXICAL e GEN_MODE_COMBINATORIAL (com permutações) |
Completo |
| Controlo | Limite de emissão | limit corta a geração após N combinações |
Completo |
| Robustez | Validação de configuração | gen_create() recusa configurações impossíveis (ciclos irresolúveis, k > group_count, etc.) |
Completo |
| Performance | Zero dependências | Apenas stdlib C | Completo |
| Testes | Suite de edge cases + fuzzing | Ciclos, auto-referências, conflitos dep-vs-excl, pesos negativos | Completo |
# O gerador itera sobre quatro eixos:
1. Valores de k (min_args .. max_args) — quantos grupos por combinação
2. k-combinações de grupos — quais grupos participam
3. Aliases dentro de cada grupo seleccionado — qual forma do argumento usar
4. Permutações da ordem dos grupos (se activado) — em que ordem os argumentos aparecem
# Cada combinação candidata só é emitida se satisfizer:
- Todas as Dependency (from requer pelo menos um de to[])
- Todas as Exclusion (a não pode coexistir com TODOS de b[])
- max_weight (soma dos pesos dos grupos seleccionados)- Compilador C11+ (gcc, clang)
# Clone o repositório
git clone https://github.com/CodeShark37/xgen.git
# Entre no diretório
cd xgen
# Compile como biblioteca estática, ou inclua xgen.c/xgen.h directamente no seu projecto
gcc -c -O2 -Wall src/xgen.c -o xgen.o#include "xgen.h"
int main(void) {
const char *help[] = { "--help", "-h" };
ArgGroup groups[] = { { help, 2, 0 } };
GenConfig cfg = {0};
cfg.groups = groups;
cfg.group_count = 1;
cfg.min_args = 1;
cfg.max_args = 1;
Generator *gen = gen_create(&cfg);
if (!gen) return 1;
const char **args;
size_t count;
while (gen_next(gen, &args, &count)) {
for (size_t i = 0; i < count; i++)
printf("%s ", args[i]);
printf("\n");
}
gen_free(gen);
return 0;
}| Passo | Função | Descrição |
|---|---|---|
| 1. Criar | gen_create(&cfg) |
Valida a configuração e devolve Generator*, ou NULL se não existir nenhuma combinação válida |
| 2. Iterar | gen_next(gen, &args, &count) |
Devolve a combinação actual e avança; false quando termina |
| 3. Consultar | gen_emitted(gen) / gen_done(gen) |
Progresso e estado da iteração |
| 4. Libertar | gen_free(gen) |
Liberta toda a memória interna |
| Campo | Tipo | Descrição |
|---|---|---|
groups / group_count |
ArgGroup* / size_t |
Grupos de argumentos disponíveis |
min_args / max_args |
size_t |
Intervalo de tamanho k das combinações |
deps / dep_count |
Dependency* / size_t |
Regras de dependência (opcional) |
excls / excl_count |
Exclusion* / size_t |
Regras de exclusão (opcional) |
max_weight |
int |
Peso cumulativo máximo (0 = sem limite) |
limit |
size_t |
Máximo de combinações a emitir (0 = ilimitado) |
mode |
GenMode |
GEN_MODE_LEXICAL ou GEN_MODE_COMBINATORIAL |
const char *verbose[] = { "--verbose", "-v", "--debug" };
ArgGroup g = { verbose, 3, /* weight */ 1 };Quando um grupo é seleccionado para uma combinação, exactamente um dos seus aliases aparece no output — a Xgen gera uma variante por alias.
Todas as regras de dependência e exclusão referenciam grupos pelo seu índice dentro do array groups[]. Usar um enum local para nomear esses índices torna as regras muito mais legíveis:
enum { G_INPUT, G_OUTPUT, G_VERBOSE };Uma dependência diz: "se from for seleccionado, pelo menos um de to[] também tem de ser".
| Forma | Semântica | Macro |
|---|---|---|
to_count == 1 |
AND clássico — from requer to |
DEP(from, to) |
to_count > 1 |
OR — from requer pelo menos um de {...} |
DEP_OR(from, ...) |
enum { G_OUTPUT, G_FORMAT };
Dependency deps[] = {
DEP(G_OUTPUT, G_FORMAT), /* --output requer --format */
};Uma exclusão diz: "a não pode coexistir com TODOS os elementos de b[] ao mesmo tempo".
| Forma | Semântica | Macro |
|---|---|---|
b_count == 1 |
Par-a-par clássico — a e b são mutuamente exclusivos |
EXCL(a, b) |
b_count > 1 |
Colectiva — a só é excluído se TODOS de {...} estiverem presentes; subconjuntos parciais são permitidos |
EXCL_OR(a, ...) |
enum { G_QUIET, G_VERBOSE };
Exclusion excls[] = {
EXCL(G_QUIET, G_VERBOSE), /* --quiet e --verbose são mutuamente exclusivos */
};Nota: dependências e exclusões não são fechadas transitivamente.
A requer BeB requer Cnão implica automaticamenteA requer C— se essa relação for necessária, deve ser declarada explicitamente.
Cada ArgGroup tem um weight (pode ser negativo). Definindo max_weight em GenConfig, apenas combinações cuja soma de pesos não exceda o limite são emitidas. max_weight == 0 desactiva a verificação.
ArgGroup groups[] = {
{ light, 2, 1 }, /* peso 1 */
{ heavy, 2, 5 }, /* peso 5 */
};
cfg.max_weight = 4; /* {light} passa; {heavy} e {light,heavy} são filtrados */| Macro | Uso |
|---|---|
DEP(from, to) |
Dependência AND simples |
DEP_OR(from, ...) |
Dependência OR (variádica) |
EXCL(a, b) |
Exclusão par-a-par |
EXCL_OR(a, ...) |
Exclusão colectiva (variádica) |
COUNT_ARGS(...) |
Helper interno usado por DEP_OR/EXCL_OR para contar argumentos variádicos |
enum { G_FORMAT, G_OUTPUT, G_COMPRESS };
Dependency deps[] = {
DEP(G_OUTPUT, G_FORMAT), /* output requer format */
DEP(G_COMPRESS, G_OUTPUT), /* compress requer output */
};
/* Válido: [--format], [--format --output], [--format --output --compress]
Inválido: [--output] (falta format), [--compress --output] (falta format) */enum { G_SAFE, G_OPT_SPEED, G_OPT_SIZE, G_PARALLEL };
Exclusion excls[] = {
/* --safe-mode não pode coexistir com TODOS os três ao mesmo tempo,
mas pode coexistir com qualquer subconjunto parcial */
EXCL_OR(G_SAFE, G_OPT_SPEED, G_OPT_SIZE, G_PARALLEL),
};Um caso de uso completo — ~20 grupos de argumentos, 21 dependências e apenas 3 regras de exclusão colectiva (em vez de 25 regras par-a-par) — está implementado em examples.c, incluindo:
/* Info é sempre standalone: exclui TODOS os outros grupos numa única regra */
EXCL_OR(G_INFO, G_INPUT, G_MODE, G_OUTPUT, G_STD, G_OPT, G_DEBUG,
G_WARN, G_WERROR, G_DEFINE, G_INCLUDE, G_ARCH, G_SANITIZE,
G_LTO, G_PIC, G_SHARED, G_STATIC, G_LIBPATH, G_LIBLINK, G_STACK);Consulte examples.c para os sete exemplos completos, do uso mais básico ao modelo do GCC9; tests.c para os casos extremos (ciclos, auto-referências, conflitos dependência-vs-exclusão, pesos negativos); e fuzz.c para o driver de stress-testing aleatório.
| Função | Descrição |
|---|---|
Generator *gen_create(const GenConfig *config) |
Cria e valida um novo gerador; NULL se a configuração for impossível |
bool gen_next(Generator *gen, const char ***out_args, size_t *out_count) |
Devolve a combinação actual e avança o estado |
void gen_free(Generator *gen) |
Liberta toda a memória associada ao gerador |
size_t gen_emitted(const Generator *gen) |
Número de combinações já emitidas |
bool gen_done(const Generator *gen) |
Se a iteração terminou |
A documentação completa de cada struct, enum e função (com exemplos individuais) está em xgen.h, escrita em Doxygen.
| Sintoma | Causa provável |
|---|---|
gen_create() devolve NULL |
Configuração impossível: ciclo de dependências que nenhum k satisfaz, min_args > max_args, max_args > group_count, ou peso mínimo já excede max_weight |
| Nenhuma combinação esperada aparece | Verifique se a dependência não é apenas implícita — a Xgen não fecha dependências transitivamente |
| Demasiadas combinações / geração lenta | Reduza max_args, defina limit, ou use GEN_MODE_LEXICAL em vez de GEN_MODE_COMBINATORIAL para evitar permutações |
| Peso negativo com resultado inesperado | Pesos negativos são somados normalmente; combinações com peso total ≤ max_weight passam, mesmo que incluam grupos "pesados" compensados por grupos de peso negativo |
Contribuições são muito bem-vindas!
- Fork o repositório
- Crie uma branch para sua feature (
git checkout -b feature/nova-funcionalidade) - Commit suas mudanças (
git commit -am 'Adiciona nova funcionalidade') - Push para a branch (
git push origin feature/nova-funcionalidade) - Abra um Pull Request
- Código em C11+
- Testes para novas funcionalidades (ver
tests.cefuzz.c) - Documentação Doxygen actualizada em
xgen.h - Commits descritivos
Encontrou um bug ou tem uma sugestão? Abra uma issue!