paulo1205
(usa Ubuntu)
Enviado em 26/10/2022 - 04:05h
Harakin escreveu:
olá, um colega da faculdade estava fazendo um projeto no Linux e acabou esquecendo de fazer a parte de Cadastro e exclusão de clientes e tô perdido no código dele, esse é meu primeiro contato tanto com Linux, quanto com C, alguém poderia me ajudar ? segue em baixo o código
Ajudar com quê, especificamente? Se você não disser qual sua dúvida, fica difícil dar a orientação correta.
Note que, como diz a orientação descrita na página da comunidade C/C++ do VoL, “
a comunidade repudia práticas ilegais e antiéticas, tais como (...)
atendimento de pedidos de respostas prontas a trabalhos, testes e exercícios escolares ”, então eu entendo que esse não é o caso. Correto?
Contudo, para não dizer que sua postagem passou em branco, eu examinei o seu código, e elenco abaixo algumas críticas construtivas sobre coisas que eu entendo que poderiam ter sido feitas de modo mais elegante, mais eficiente ou mesmo mais adequado ao jargão da comunidade de programadores em C.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <openssl/sha.h>
#include <sys/types.h>
#include <sys/stat.h>
int OWNER_USER = 1;
char usuario[128];
struct Cadastro {
char login[128];
int id;
char hash[128];
char nome[128];
char cargo[128];
};
“Cadastro” é um nome muito vago. Cadastro de quê? Sem ler o enunciado da questão, a gente pode no máximo chutar.
Além disso, o nome “cadastro” dá a ideia de que haverá múltiplos itens de características semelhantes cadastrados. Será que o nome desse tipo de dados que foi criado não deveria ser trocado para algo como “
item_cadastro ”, “
item_cadastro_usuario ” ou mesmo simplesmente “
dados_usuario ”?
Obviamente, se o nome do tipo for alterado aqui, a mudança teria de ser refletida em todos os lugares em que o nome do tipo criado tiver sido empregado no restante do código.
struct Estoque {
char produto[128];
int quantidade;
};
Observação semelhante aqui: este tipo de dados se refere à representação de um estoque, ou meramente a um item de estoque? Se é só a um item, o nome provavelmente poderia refletir essa situação.
struct Loja {
char produto[128];
int quantidade;
};
Mais uma vez, uma situação envolvendo o nome do tipo de dados. Contudo, neste caso de agora é impossível não notar a semelhança estrutural entre este tipo de dados e o tipo de dados imediatamente anterior. Possivelmente (mas não necessariamente) isso indica que os dois tipos poderiam ser fundidos num só, com um nome de tipo só, mas que tivesse instanciamentos diferentes, dependendo de se o item está sendo usado para implementar um estoque genérico ou o estoque específico de uma loja (se é que até essa diferença de uso faz sentido; eu ainda não olhei o restante do programa para saber).
void printCadastro(struct Cadastro *ptr)
{
printf("Nome: %s\n", ptr->nome);
printf("Id: %d\n", ptr->id);
printf("Hash: %s\n", ptr->hash);
printf("Nome: %s\n", ptr->nome);
printf("Cargo: %s\n", ptr->cargo);
}
int pegarTAM(FILE **fptr)
Tanto aqui quanto em outras funções do programa, usar um ponteiro para ponteiro para arquivo como tipo do parâmetro usado para acesso ao arquivo parece fundamentalmente errado, pois a função não faz nenhuma operação que justifique o uso de ponteiro para ponteiro em vez de um ponteiro comum.
O uso de ponteiro para ponteiro somente faria sentido caso a função fizesse algo como abrir ou reabrir o arquivo, e tal operação tivesse se ser refletida para quem chamou a função. Claramente não é o caso, nem aqui nem em outras funções que usam o arquivo de modo semelhante.
Minha sugestão é que você troque todos os casos semelhantes a
void funcao(FILE **ppf){
char buffer[200];
/* ... */
fgets(buffer, sizeof buffer, *ppf); // Note que “*ppf” se lê “valor do conteúdo apontado por ppf”. Se você se refere ao valor do conteúdo apontado, por que não passar diretamente tal valor, especialmente se esse valor não for alterado ao longo da função?
/* ... */
fputc(':', *ppf);
fputc('1', *ppf);
fputc(':', *ppf);
// Note que nenhuma das operações acima testa se houve erro ou não.
/* ... */
} por algo mais convencional (e sensato), tal como o seguinte.
bool funcao(FILE *pf){
char buffer[200];
/* ... */
if(!fgets(buffer, sizeof buffer, pf)) // Testa para ver se a leitura teve erro (e usa o diretamente valor do ponteiro para arquivo, não um “valor de conteúdo apontado por ponteiro para ponteiro para o arquivo”).
return false; // Função retorna false caso a operação tenha falhado, permitindo que que a chamou trate o eventual erro.
// (Mesmo que seja para imprimir uma mensagem de erro, normalmente é preferível que isso seja tratado num nível mais externo do programa, não na própria função que faz a leitura.)
/* ... */
if(fputs(":1:", pf)==EOF) // Escreve os três caracteres com uma só chamada a função, e verifica que a escrita ocorreu com sucesso.
return false; // Caso a verificação seja negativa, sai indicando erro.
/* ... */
return true; // Se chegou até aqui, sai indicando que a função foi bem sucedida.
}
{
char *buffer = (char *) malloc(4096 * sizeof(char));
Aqui você faz a alocação dinâmica de um
buffer para uma operação de leitura. Eu tenho comentários sobre como isso é feito, mas antes de os tecer, permita-me questionar a decisão de ter um
buffer com alocação dinâmica, principalmente em virtude de que outras funções do programa que usam
buffers semelhantes não recorrem a alocação dinâmica e em tempo de execução, mas usam alocação em tempo de compilação e em memória automática, que é muito mais simples. Parece inconsistente ter algo diferente somente nesta função.
Você não disse se está usando um compilador C ou C++. O único caso em que a construção imediatamente acima faria algum sentido é de você tivesse de usar um compilador C++ para compilar um código que tivesse de ser escrito em C.
A linha acima não se justifica num programa em C usando um compilador C pelos seguintes motivos:
•
sizeof(char) é sempre, por definição, igual a
1 . Assim sendo, a multiplicação “
4096*sizeof(char) ” é completamente redundante; bastaria escrever “
4096 ” como argumento na chamada a
malloc ().
• A conversão explícita do valor de retorno da chamada a
malloc () é desnecessária, pois em C o tipo
void * , que é o tipo de dado retornando pela função, é sempre automaticamente conversível em qualquer outro tipo de ponteiro. Explicitar algo que a linguagem faz automaticamente é redundante, e pode induzir a erros, em vez de os evitar.
Por outro lado, se o código for em C++ (usando, obviamente, um compilador C++), é verdade que a conversão implícita de
void * para
char * não aconteceria, já que o C++ não possui tal conversão. Entretanto, o C++ também rejeita, em geral, o uso de das funções de biblioteca
malloc (),
realloc (),
calloc () e
free (), dando preferência aos operadores
new e
delete para elementos únicos ou
new [] e
delete[] para
arrays , ou, em muitos casos, preferencialmente um tipo tipo de dados da biblioteca de
templates , tal como
std::vector num caso geral, ou
std::string se você for lidar sobretudo com texto, uma vez que esta duas última abordagens tornam a manipulação do
buffer muito mais parecida com o caso de alocação em tempo de compilação e em memória automática (embora não o sejam). Em C++, uma função na forma
void func(){
char *buffer=(char *)malloc(4096);
/* ... */
if(fgets(buffer, 4096, stdin)!=NULL){ // Acesso ao buffer como um todo como destino de uma operação de leitura, que exige um ponteiro. O tamanho 4096 não tem nenhum vínculo com o tipo do ponteiro, e tem de ser lembrado externamente pelo programador (ou mantido em um variável, mas também externa à alocação que foi feita).
size_t length=strlen(buffer); // acesso ao buffer como um todo.
if(length>0 && buffer[length-1]=='\n') // Acesso a um elemento específico.
buffer[--length]='\0'; // Acesso a elemento específico para eliminar a quebra de linha ao fim da string.
puts(buffer); // Chama função de impressão que também espera um ponteiro como argumento.
}
/* ... */
free(buffer);
} poderia assumir uma das seguintes formas.
// C++: trocar malloc() de múltiplos elementos por new [], e free() por delete[]. Outras operações continuam idênticas.
void func(){
char *buffer=new char[4096];
/* ... */
if(fgets(buffer, 4096, stdin)!=NULL){ // Acesso ao buffer como um todo como destino de uma operação de leitura, que exige um ponteiro. O tamanho 4096 não tem nenhum vínculo com o tipo do ponteiro, e tem de ser lembrado externamente pelo programador (ou mantido em um variável, mas também externa à alocação que foi feita).
size_t length=strlen(buffer); // acesso ao buffer como um todo.
if(length>0 && buffer[length-1]=='\n') // Acesso a um elemento específico.
buffer[--length]='\0'; // Acesso a elemento específico para eliminar a quebra de linha ao fim da string.
puts(buffer); // Chama função de impressão que também espera um ponteiro como argumento.
}
/* ... */
delete[] buffer;
} // C++ (preferível num caso geral): usar o tipo template std::vector, que cuida automaticamente de fazer alocação e desalocação.
void func(){
std::vector<char> buffer(4096); // Declara um vetor e pede que ele seja construído com o tamanho inicial de 4096 bytes.
/* ... */
if(fgets(buffer.data(), buffer.size(), stdin)!=NULL){ // Acesso ao bloco de memória alocado ao buffer (buffer.data()) como destino de uma operação de leitura, que exige um ponteiro. Note, porém, que o tamanho do buffer agora é uma propriedade do dado (buffer.size()).
size_t length=strlen(buffer.data()); // Acesso à memória alocada para o buffer como um todo; strlen() também exige um ponteiro.
if(length>0 && buffer[length-1]=='\n') // Acesso a um elemento específico. Aqui não tem alteração visual em relação aos mostrados acima.
buffer[--length]='\0'; // Acesso a elemento específico para eliminar a quebra de linha ao fim da string.
puts(buffer.data()); // Chama função de impressão que também espera um ponteiro como argumento.
}
/* ... */
// Note que não precisa se preocupar com desalocação.
} // C++ (preferível quando trabalhar especificamente com strings de texto): usar o tipo std::string, que também cuida automaticamente de fazer alocação e desalocação.
void func(){
std::string str; // Declara a string sem se preocupar com tamanho, pois o tipo de dados conta com operações que provocam realocações automáticas quando necessário.
/* ... */
if(std::getline(std::cin, str)){ // Leitura usando função da biblioteca de I/O em C++, em vez das funções do C, para lidar melhor com o tipo string.
// A função std::getline() não retém a quebra de linha (lê-a, mas a descarta), mas vou manter o código abaixo só para dar um exemplo de como seria.
size_t length=str.length();
if(length>0 && buffer[length-1]=='\n'){ // Acesso a um elemento específico. Aqui não tem alteração visual em relação aos mostrados acima (outra forma de acesso ao último elemento para strings é “buffer.back()”; ex.: “if(buffer.back()=='\n'){ /* ... */ }”).
buffer.resize(--length); // Eliminar a quebra de linha efetivamente altera o tamanho da string (poderia ser feito sem a variável length, com algo como “str.resize(str.length()-1);”
puts(buffer.c_str()); // Chama função de impressão que também espera um ponteiro como argumento (mas a forma tradicional de fazer em C++ seria diferente: algo como “std::cout << buffer << '\n';”).
}
/* ... */
// Note que não precisa se preocupar com desalocação.
}
Acabou ficando muito mais tarde do que eu esperava, e daqui a pouco eu tenho de ir trabalhar. Se eu puder, continuo respondendo amanhã à noite.
char *ch;
int TAM = 0;
fread(buffer, 1, 4096 * sizeof(char), *fptr);
rewind(*fptr);
ch = strtok(buffer, "\n");
while (ch != NULL) {
ch = strtok(NULL, "\n");
TAM++;
}
free(buffer);
return TAM;
}
void pagina_login(struct Cadastro *ptr, int TAM)
{
int login_flag = 0, owner_user = 1, t = 3, i = 0, j = 0, key = -1;
char login[128], senha[128];
unsigned char tmp_hash[SHA256_DIGEST_LENGTH];
char hex[SHA256_DIGEST_LENGTH * 2];
printf("------------------------------------------------\n"
"Login: ");
scanf("%128s", login);
if ((strcmp("owner", login) == 0)) {
owner_user = 0;
}
for (i = 0; i < TAM; ++i) {
if ((strcmp(login, (ptr + i)->login) == 0)) {
key = i;
break;
}
if ((strcmp(login, (ptr + i)->login) != 0)) {
if (i == TAM - 1) {
printf("Usuário não encontrado.\n");
printf("------------------------------------------------\n");
exit(EXIT_FAILURE);
}
}
}
do {
printf("Senha: ");
scanf("%128s", senha);
SHA256((const unsigned char *) senha, strlen(senha), tmp_hash);
for (i = 0, j = 0; i < SHA256_DIGEST_LENGTH; ++i, j += 2) {
sprintf((hex + j), "%02x", tmp_hash[i]);
}
login_flag = (strcmp((ptr + key)->hash, hex) == 0) ? 0 : 1;
if (login_flag == 1) {
printf("Senha Incorreta.\n");
}
else {
printf("Senha Correta.\n");
printf("------------------------------------------------\n");
strcpy(usuario, (ptr + key)->login);
if (owner_user == 0)
OWNER_USER = 0;
break;
}
t--;
} while (t > 0);
}
void cadastrar(FILE **fptr)
{
if (OWNER_USER != 0) {
printf( "\nOperação Negada.\n\n");
return;
}
int i = 0, j = 0;
char login[128], senha[128], nome[128], cargo[128], hex[64];
unsigned char hash[32];
fseek(*fptr, 0, SEEK_END);
srand(time(NULL));
printf("Cadastrando novo funcionário.\n");
printf("Novo login: ");
scanf("%128s", login);
printf("Nova senha: ");
scanf("%128s", senha);
printf("Nome:" );
scanf("%128s", nome);
printf("Cargo:" );
scanf("%128s", cargo);
fputs(login, *fptr);
fputc(':', *fptr);
fputc('1', *fptr);
fputc(':', *fptr);
SHA256(senha, strlen(senha), hash);
for (i = 0, j = 0; i < SHA256_DIGEST_LENGTH; ++i, j += 2) {
sprintf((hex + j), "%02x", hash[i]);
}
fputs(hex, *fptr);
fputc(':', *fptr);
fputs(nome, *fptr);
fputc(':', *fptr);
fputs(cargo, *fptr);
fputc('\n', *fptr);
rewind(*fptr);
}
void preencherCadastro(struct Cadastro *ptr, FILE **fptr, int TAM)
{
int i = 0;
char buffer[4096];
char *ch;
for (i = 0; i < TAM; ++i) {
fgets(buffer, sizeof(buffer), *fptr);
ch = strtok(buffer, ":");
strcpy((ptr + i)->login, ch);
ch = strtok(NULL, ":");
(ptr + i)->id = *ch - 48;
ch = strtok(NULL, ":");
strcpy((ptr + i)->hash, ch);
ch = strtok(NULL, ":");
strcpy((ptr + i)->nome, ch);
ch = strtok(NULL, ":");
strcpy((ptr + i)->cargo, ch);
}
}
void removerLoja(struct Loja *l_ptr)
{
int quantidade = 0;
char produto[128];
printf("Digite a o produto e a quantidade a ser removida da loja.\n");
scanf("%128s", produto);
for (int i = 0; i < 256; ++i) {
if (strcmp(produto, (l_ptr + i)->produto) == 0) {
scanf("%d", &quantidade);
if (quantidade > (l_ptr + i)->quantidade) {
printf("Operação não permitida.\n");
printf("Quantidade maior que a da loja (%d)\n",
(l_ptr + i)->quantidade);
return;
} else {
(l_ptr + i)->quantidade = (l_ptr + i)->quantidade - quantidade;
printf("Nova quantidade na loja de %d.\n", (l_ptr + i)->quantidade);
return;
}
}
}
printf("Produto não encontrado.\n");
}
void adicionarProdutoEstoque(struct Estoque *ptr)
{
for (int i = 0; i < 256; ++i) {
if ((ptr + i)->quantidade == 0) {
printf("Digite o nome do produto e sua quantidade.\n");
scanf("%128s", (ptr + i)->produto);
scanf("%d", &((ptr + i)->quantidade));
printf("%d produto(s) %s adicionados ao estoque.\n\n\n",
(ptr + i)->quantidade, (ptr + i)->produto);
return;
}
}
}
void adicionarLoja(struct Estoque *e_ptr, struct Loja *l_ptr)
{
char produto[128];
int quantidade;
printf("Produto que deseja adicionar a loja.\n");
scanf("%128s", produto);
for (int i = 0; i < 256; ++i) {
if ((strcmp(produto, (e_ptr + i)->produto) == 0)) {
printf("Qual quantidade deseja adicionar a loja: ");
scanf("%d", &quantidade);
if (quantidade > (e_ptr + i)->quantidade) {
printf("Operação não permitida.\n");
printf("Quantidade maior que a do estoque (%d)\n",
(e_ptr + i)->quantidade);
return;
} else {
for (int j = 0; j < 256; ++j) {
if ((l_ptr + j)->quantidade == 0) {
strcpy((l_ptr + j)->produto, produto);
(l_ptr + j)->quantidade = quantidade;
break;
}
}
printf("%d produtos de %s adicionados a loja.\n",
quantidade, (e_ptr + i)->produto);
(e_ptr + i)->quantidade = (e_ptr + i)->quantidade - quantidade;
printf("Quantidade restante no estoque: %d\n", (e_ptr + i)->quantidade);
return;
}
}
}
printf("Produto não encontrado no estoque.\n");
}
FILE **preencherArquivo(struct Cadastro *cadastro, FILE **fptr, char *argv)
{
FILE **f_ptr = (FILE **) malloc(sizeof(FILE *));
char zeros[128];
memset(zeros, 0, 128 * sizeof(char));
*f_ptr = freopen(NULL, "w", *fptr);
for (int i = 0; i < 3; ++i) {
if ((strcmp(zeros, (cadastro + i)->login) == 0)) {
continue;
} else {
fputs((cadastro + i)->login, *f_ptr);
fputc(':', *fptr);
fputc((cadastro + i)->id + 48, *f_ptr);
fputc(':', *fptr);
fputs((cadastro + i)->hash, *f_ptr);
fputc(':', *fptr);
fputs((cadastro + i)->nome, *f_ptr);
fputc(':', *fptr);
fputs((cadastro + i)->cargo, *f_ptr);
}
}
return f_ptr;
}
void removerUsuario(FILE **fptr, struct Cadastro *cadastro, char *argv)
{
if (OWNER_USER != 0) {
printf( "\nOperação Negada.\n\n");
return;
}
char login[128];
printf("Digite o nome do login: ");
scanf("%128s", login);
for (int i = 0; i < 3; ++i) {
printf("user: %s\n", (cadastro + i)->login);
if ((strcmp(login, (cadastro + i)->login) == 0)) {
memset((cadastro + i), 0, 128);
fptr = preencherArquivo(cadastro, fptr, argv);
return;
}
}
printf("Usuário não encontrado.\n");
}
void menu(FILE **fptr, struct Cadastro *cadastro, char *argv)
{
printf("Bem vindo usuário %s.\n", usuario);
struct Estoque *estoque = (struct Estoque *)
malloc(256 * sizeof(struct Estoque));
struct Loja *loja = (struct Loja *)
malloc(256 * sizeof(struct Loja));
int choice = 0;
printf("\n\n\n\n");
while (1) {
printf("Opções de usuário:\n");
printf("[1] Cadastrar novo funcionário.\n");
printf("[2] Remover funcionário.\n");
printf("[3] Adicionar produto ao estoque.\n");
printf("[4] Adicionar produto a loja.\n");
printf("[5] Remover produto da loja.\n");
printf("[*] Sair do Programa.\n");
scanf("%d", &choice);
#ifdef WINDOWS
system("cls");
#else
system("clear");
#endif
switch (choice) {
case 1:
cadastrar(fptr);
break;
case 2:
removerUsuario(fptr, cadastro, argv);
break;
case 3:
adicionarProdutoEstoque(estoque);
break;
case 4:
adicionarLoja(estoque, loja);
break;
case 5:
removerLoja(loja);
break;
default:
exit(EXIT_SUCCESS);
}
}
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Error in argc.\n");
exit(EXIT_FAILURE);
}
struct stat st;
if (stat(argv[1], &st)) {
fprintf(stderr, "Erro no arquivo.\n");
exit(EXIT_FAILURE);
}
FILE *fptr = fopen(argv[1], "r+");
int TAM = pegarTAM(&fptr);
struct Cadastro *cadastro = (struct Cadastro *) malloc(TAM * sizeof(struct Cadastro));
preencherCadastro(cadastro, &fptr, TAM);
pagina_login(&cadastro[0], TAM);
menu(&fptr, cadastro, argv[1]);
free(cadastro);
return 0;
}
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)