primeiro commit
This commit is contained in:
parent
6b18653de4
commit
57081c0e82
4 changed files with 257 additions and 1 deletions
47
README.md
47
README.md
|
@ -1,3 +1,48 @@
|
|||
# hdoc
|
||||
|
||||
Emula um dos usos do mecanismo de "here doc" (didático)
|
||||
Emula um dos usos do mecanismo de "here doc" (didático). Equivale à linhas de comando como:
|
||||
|
||||
```
|
||||
here document
|
||||
↓
|
||||
cat << END > arquivo
|
||||
```
|
||||
|
||||
Mas...
|
||||
|
||||
- Não utiliza uma palavra de término (termina com `Ctrl+D`);
|
||||
- Por padrão, escreve apenas em novos arquivos (termina com erro se o arquivo existir);
|
||||
- Com opções, a sobrescrita ou o *append* de arquivos existentes pode ser forçada.
|
||||
|
||||
## Como utilizar
|
||||
|
||||
```
|
||||
Here Document (hdoc) 0.1
|
||||
|
||||
Uso: hdoc [OPÇÕES] ARQUIVO
|
||||
|
||||
Sem OPÇÕES, cria um novo ARQUIVO para receber linhas
|
||||
digitadas no terminal.
|
||||
|
||||
OPÇÕES:
|
||||
-o Força abertura de ARQUIVO para sobrescrevê-lo.
|
||||
-a Força abertura de ARQUIVO para append.
|
||||
-h Exibe esta ajuda.
|
||||
```
|
||||
|
||||
## Como compilar
|
||||
|
||||
No diretório da sua escolha...
|
||||
|
||||
```
|
||||
:~$ git clone https://bolha.dev/blau_araujo/hdoc.git
|
||||
:~$ cd hdoc
|
||||
:~/hdoc$ gcc -Wall -Wextra hdoc.c messages.c -o hdoc
|
||||
```
|
||||
|
||||
O executável (`hdoc`) pode ser movido ou linkado para um diretório pessoal de executáveis ou para `/usr/local/bin`.
|
||||
|
||||
## Importante!
|
||||
|
||||
Este programa é apenas um exemplo didático para as pessoas que estão aprendendo C com o [Curso Básico da Linguagem C](https://www.youtube.com/playlist?list=PLXoSGejyuQGrDX08GVrQHAhh4j3KJ4iYN): use por sua conta e risco!
|
||||
|
||||
|
|
120
hdoc.c
Normal file
120
hdoc.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Compilar com: gcc -Wall hdoc.c messages.c -o hdoc
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "messages.h"
|
||||
|
||||
typedef enum {
|
||||
OPT_ERROR = -1,
|
||||
OPT_NAME,
|
||||
OPT_OVERWRITE,
|
||||
OPT_APPEND,
|
||||
OPT_HELP
|
||||
} Option;
|
||||
|
||||
int parse_arg(char *arg); // Trata argumentos na linha de comando
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
char *file = NULL;
|
||||
char *mode = NULL;
|
||||
|
||||
// Verificação do número de argumentos...
|
||||
if (argc == 1 || argc > 3) {
|
||||
// Só pode haver 2 ou 3 argumentos...
|
||||
print_error(MSG_ARGC_ERROR);
|
||||
return print_usage(EXIT_FAILURE, argv[0]);
|
||||
} else if (argc == 2) {
|
||||
// Invocação com 2 argumentos...
|
||||
switch (parse_arg(argv[1])) {
|
||||
case OPT_HELP:
|
||||
// Imprime ajuda e termina...
|
||||
return print_usage(EXIT_SUCCESS, argv[0]);
|
||||
case OPT_NAME:
|
||||
// Arquivo existe?
|
||||
if (access(argv[1], F_OK) == 0) {
|
||||
// Sem opções, só arquivos novos!
|
||||
print_error(MSG_FILE_EXISTS);
|
||||
return print_usage(EXIT_FAILURE, argv[0]);
|
||||
}
|
||||
// Nome do novo arquivo...
|
||||
file = argv[1];
|
||||
mode = "w";
|
||||
break;
|
||||
default:
|
||||
// Nome do arquivo não pode começar com '-'!
|
||||
print_error(MSG_INVALID_ARG);
|
||||
return print_usage(EXIT_FAILURE, argv[0]);
|
||||
}
|
||||
} else {
|
||||
// Invocação com 3 argumentos...
|
||||
for (int i = 1; argv[i] != NULL; i++) {
|
||||
switch (parse_arg(argv[i])) {
|
||||
case OPT_NAME:
|
||||
// Só pode haver uma definição de nome...
|
||||
if (file == NULL) file = argv[i];
|
||||
break;
|
||||
case OPT_OVERWRITE:
|
||||
// Só pode haver uma definição de modo...
|
||||
if (mode == NULL) mode = "w";
|
||||
break;
|
||||
case OPT_APPEND:
|
||||
// Só pode haver uma definição de modo...
|
||||
if (mode == NULL) mode = "a";
|
||||
break;
|
||||
case OPT_HELP:
|
||||
// Exibe ajuda e termina com sucesso...
|
||||
return print_usage(EXIT_SUCCESS, argv[0]);
|
||||
case OPT_ERROR:
|
||||
// Algum dos argumentos é inválido...
|
||||
print_error_fmt(MSG_INVALID_FMT, argv[i]);
|
||||
return print_usage(EXIT_FAILURE, argv[0]);
|
||||
}
|
||||
}
|
||||
// Tem que haver um nome e um modo depois do loop...
|
||||
if (file == NULL || mode == NULL) {
|
||||
print_error(MSG_INCORRECT_ALL);
|
||||
return print_usage(EXIT_FAILURE, argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
FILE *fstr = fopen(file, mode);
|
||||
if (!fstr) {
|
||||
perror(MSG_FOPEN_ERROR);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char line[BUFSIZ];
|
||||
puts(MSG_EOF_HINT);
|
||||
while (1) {
|
||||
printf(MSG_PROMPT);
|
||||
if (fgets(line, BUFSIZ, stdin) == NULL) break;
|
||||
fprintf(fstr, "%s", line);
|
||||
}
|
||||
|
||||
fclose(fstr);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int parse_arg(char *arg) {
|
||||
// Argumento iniciado com '-'...
|
||||
if (arg[0] == '-') {
|
||||
if (strcmp(arg, "-a") == 0) {
|
||||
return OPT_APPEND;
|
||||
} else if (strcmp(arg, "-o") == 0) {
|
||||
return OPT_OVERWRITE;
|
||||
} else if (strcmp(arg, "-h") == 0) {
|
||||
return OPT_HELP;
|
||||
} else {
|
||||
// Argumento inválido!
|
||||
return OPT_ERROR;
|
||||
}
|
||||
}
|
||||
// Sem traço é nome de arquivo...
|
||||
return OPT_NAME;
|
||||
}
|
63
messages.c
Normal file
63
messages.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "messages.h"
|
||||
|
||||
// Mensagens de uso e ajuda
|
||||
const char *MSG_USAGE =
|
||||
"Here Document (hdoc) 0.1\n\n"
|
||||
"Uso: %s [OPÇÕES] ARQUIVO\n\n"
|
||||
"Sem OPÇÕES, cria um novo ARQUIVO para receber linhas\n"
|
||||
"digitadas no terminal.\n\n"
|
||||
"OPÇÕES:\n"
|
||||
" -o Força abertura de ARQUIVO para sobrescrevê-lo.\n"
|
||||
" -a Força abertura de ARQUIVO para append.\n"
|
||||
" -h Exibe esta ajuda.\n\n";
|
||||
|
||||
const char *MSG_EOF_HINT = "Tecle Ctrl+D para terminar...";
|
||||
const char *MSG_PROMPT = "> ";
|
||||
|
||||
// Mensagens de erro
|
||||
const char *MSG_ARGC_ERROR = "Número incorreto de argumentos!\n\n";
|
||||
const char *MSG_FILE_EXISTS = "Arquivo já existe!\n\n";
|
||||
const char *MSG_INVALID_ARG = "Argumento inválido!\n\n";
|
||||
const char *MSG_INVALID_FMT = "Argumento inválido (%s)!\n\n";
|
||||
const char *MSG_INCORRECT_ALL = "Argumentos incorretos!\n\n";
|
||||
const char *MSG_FOPEN_ERROR = "Erro na abertura do arquivo!\n\n";
|
||||
|
||||
// Estilo ANSI: negrito + amarelo
|
||||
#define STYLE_ERROR "\033[1;33m"
|
||||
#define STYLE_RESET "\033[0m"
|
||||
|
||||
// Retorna sucesso se stderr estiver ligada ao terminal.
|
||||
int is_tty_stderr() {
|
||||
return isatty(fileno(stderr));
|
||||
}
|
||||
|
||||
// Imprime mensagens de erro sem argumentos.
|
||||
void print_error(const char *msg) {
|
||||
if (is_tty_stderr())
|
||||
fprintf(stderr, STYLE_ERROR "%s" STYLE_RESET, msg);
|
||||
else
|
||||
fprintf(stderr, "%s", msg);
|
||||
}
|
||||
|
||||
// Imprime mensagens de erro com argumentos.
|
||||
void print_error_fmt(const char *fmt, const char *arg) {
|
||||
if (is_tty_stderr()) {
|
||||
fprintf(stderr, "%s", STYLE_ERROR);
|
||||
fprintf(stderr, fmt, arg);
|
||||
fprintf(stderr, "%s", STYLE_RESET);
|
||||
} else {
|
||||
fprintf(stderr, fmt, arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Imprime versão e ajuda e retorna status.
|
||||
int print_usage(int status, char *prog_name) {
|
||||
FILE *stream = stderr;
|
||||
if (status == EXIT_SUCCESS) stream = stdout;
|
||||
fprintf(stream, MSG_USAGE, prog_name);
|
||||
return status;
|
||||
}
|
28
messages.h
Normal file
28
messages.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Strings definidas em messages.c
|
||||
*/
|
||||
#ifndef MESSAGES_H
|
||||
#define MESSAGES_H
|
||||
|
||||
// Mensagens de uso e ajuda
|
||||
extern const char *MSG_USAGE;
|
||||
extern const char *MSG_EOF_HINT;
|
||||
extern const char *MSG_PROMPT;
|
||||
|
||||
// Mensagens de erro
|
||||
extern const char *MSG_ARGC_ERROR;
|
||||
extern const char *MSG_FILE_EXISTS;
|
||||
extern const char *MSG_INVALID_ARG;
|
||||
extern const char *MSG_INVALID_FMT;
|
||||
extern const char *MSG_INCORRECT_ALL;
|
||||
extern const char *MSG_FOPEN_ERROR;
|
||||
|
||||
// Impressão estilizada condicional
|
||||
int is_tty_stderr(void);
|
||||
void print_error(const char *msg);
|
||||
void print_error_fmt(const char *fmt, const char *arg);
|
||||
|
||||
// Impressão da ajuda
|
||||
int print_usage(int status, char *prog_name);
|
||||
|
||||
#endif // MESSAGES_H
|
Loading…
Add table
Reference in a new issue