/*! * \file ceui.c * \brief Módulo da interface com usuário. * * \date 02-05-2012 16:36:35 * \version 0.0.1 * \author Alessandro Elias, ae11@inf.ufpr.br * \author Welton Pereira Martins, wpm11@inf.ufpr.br * * \details Este módulo possui todas as funcões que interagem com o usuário, * é o TUI (text user interface). */ #include #include #include #include /* para strcasecmp() */ #include /* para read() e poll()*/ #include #include /* para setupterm() */ #include /* tigetnum() */ #include "mydebug.h" #include "ceui.h" #include "fila.h" #include "pilha.h" #include "lista.h" #include "log.h" /*! * Variável blobal, recebe valor da função isatty(). * \details isatty() retorna 1 quando file descriptor * esta conectado a um terminal, 0 caso * contrário. */ bool g_bStdinConnectedTotty; /*! Variável global, armazena número de colunas do terminal do usuário. */ int g_ncols = DEFAULT_COLS; /*! Variável global, armazena número de linhas do terminal do usuário. */ int g_nlines = DEFAULT_LINES; /* __________________________________________________________________________________ */ /*! * Ponto de inicialização do program. * * \return 0 em caso o programa foi executado com sucesso, caso contrário * código do erro ocorrido. * * \sa InicFila, InicLista, InicPilha, Greeting, PrintMenu, MoreInput, OpenLog, CloseLog, LogMessage, WaitReturn, ParseOption */ int main(void) { char szOpt[4]; /* armazena entrada do usuario, opcao do menu */ char *p; /* ponteiro para final da entrada, LF '\n' */ enum eOptMenu opt; /* opcao do menu */ int n, err; /* n, numero de caracteres lido, err ultimo erro do sistema */ struct TipoFila fila; struct TipoLista lista; struct TipoPilha pilha; OpenLog(); if( !InicFila(&fila) ) return ERR_INIC_FILA; if( !InicLista(&lista) ) return ERR_INIC_LISTA; if( !InicPilha(&pilha) ) return ERR_INIC_PILHA; /* Obtenha tamanho da screen do usuario */ if( !setupterm(NULL, fileno(stdout), (int*)NULL) ) /* preencha dados do terminal do usuario */ { g_ncols = tigetnum("cols"); g_nlines = tigetnum("lines"); if( g_ncols == 0 ) g_ncols = DEFAULT_COLS; if( g_nlines == 0 ) g_nlines = DEFAULT_LINES; } /* * Obtenha valor booleano se usuario redirecionou stdin. * (Ver descricao na secao global) */ g_bStdinConnectedTotty = isatty(STDIN_FILENO); system("clear"); Greeting(); PrintMenu(); /* set todo o buffer com final de entrada */ memset(szOpt, '\0', sizeof(szOpt)); n = read(STDIN_FILENO, szOpt, sizeof(szOpt)-1); err = errno; if( n == -1 ) { PERRLN("Erro 'main'"); LogMessage("Erro 'main': %s.\n", strerror(err)); *szOpt = '\0'; WaitReturn(); } if( (p = strchr(szOpt, '\n')) != NULL ) *p = '\0'; /* * Verifique se usuario entrou mais dados que * o limite do buffer, se sim consuma o restante. * Isto deve ocorrer soment se stdin estiver connectado * a um terminal. */ if( g_bStdinConnectedTotty && MoreInput() ) { fprintf(stderr, "ATENCAO! Char's execendes de '%s' foram truncados.\n", szOpt); LogMessage("ATENCAO! Char's execendes de '%s' foram truncados.\n", szOpt); WaitReturn(); } /* * atoi() se encaregara de nos retornar o inteiro. * Em caso usuario entrou algo arbitrario do que um * inteiro in ascii, atoi retornara 0, o que não e * identificado em ParseMenu(). */ opt = (enum eOptMenu)atoi(szOpt); while( opt != oExit ) { /* Se usuario ja entrou um comando, entao processe ele ate que não haja mais comandos. */ while( (opt = ParseOption(szOpt, opt, &fila, &lista, &pilha)) != oNone ) ; system("clear"); Greeting(); PrintMenu(); n = read(STDIN_FILENO, szOpt, sizeof(szOpt)-1); err = errno; if( n == -1 ) { PERRLN("Erro 'main'"); LogMessage("Erro 'main': %s.\n", strerror(err)); *szOpt = '\0'; WaitReturn(); }/* if n == -1 */ if( (p = strchr(szOpt, '\n')) != NULL ) *p = '\0'; if( g_bStdinConnectedTotty && MoreInput() ) { fprintf(stderr, "ATENCAO! Char's execendes de '%s' foram truncados.\n", szOpt); LogMessage("ATENCAO! Char's execendes de '%s' foram truncados.\n", szOpt); WaitReturn(); } opt = (enum eOptMenu)atoi(szOpt); }/* while */ /* feche streams */ CloseLog(); return EXIT_SUCCESS; }/* main */ /*! * função verifica se ha mais dados a serem lidos da * entrada padrão. * * \remark Esta decisão de implementação fez-se * necessária, uma vez que decidimos que o usuário * é livre para dar entrada ao programa sem * filtrar o que o usuário entrou. O comportamento * de read() em caso a entrada do usuário é maior que * o buffer passado a ela, é de: na subsequente chamada * a ela, irá retornar o restante da entrada do usuário, * até que possa ser lido toda entrada do usuário, uma vez * todo o buffer foi consumido, subsequente chamada irá entrar * em modo de espera até que seja dada uma outra entrada, em outras * palavras, quando tem mais dados no buffer do stdin, as chamadas * ao read() retornara imediatamente, até que não reste mais nada no * stdin. */ bool MoreInput(void) { struct pollfd fds; /* estrutura com os dados a chamada do sistema low-level */ fds.fd = STDIN_FILENO; /* fileno(stdin) retorna zero, afinal 0 e o descriptor da entrada padrao. */ fds.events = POLLIN; /* evento que estamos interessado, neste caso se ha dados em stdin. */ fds.revents = 0; /* Nao estamos interesados em eventos de retorno. */ /* * poll() retorna o numero de fds que tem * o status de interesse, caso nao haja dados em stdin * poll retorna 1 (neste caso, temos somente um fd), 0 * caso contrario. */ return( poll(&fds, 1, 0) == 1 ); }/* MoreInput */ /*! * \brief Consome dados da entrada padrão. * Isto fará com que os dados a mais * que o usuário entrou sejam ignorados. * A leitura byte a byte faz-se necessária, pois o programa * pode estar trabalhando com um arquivo de entrada, assim * podemos identificar cada final de entrada com LF '\n'. * * \remarks Note que esta função só é útil quando o programa esta interagindo * com o usuário. Nesta condição podemos fazer o programa ignorar a * entrada que excede algum buffer. */ void EmptyStdIn(void) { char ch = 0; /* nao faca nada com os dados lidos, apenas ignore-os * ate que seja encontrado LF ('\n'). */ while( ch != '\n' && read(STDIN_FILENO, &ch, 1) > 0 ) ; }/* EmptyStdIn */ /*! * Imprime titulo do programa. */ void Greeting(void) { static const char *szTitle[] = { "CADASTRO DE", "ESTUDANTES v 0.0.1" }; char buff[256]; int x, nl1, nl2; int centerl1, centerl2; #undef TOTAL #define TOTAL 48 memset(buff, '-', 254); buff[255] = '\0'; /* calcule centro da tela */ nl1 = strlen(szTitle[0]); /* Tamanho da linha 1 do titulo */ nl2 = strlen(szTitle[1]); /* Tamanho da linha 2 do titulo */ centerl1 = (TOTAL - nl1) / 2; /* centralize linha 1 no meio de +---...---+ */ centerl2 = (TOTAL - nl2) / 2; /* centralize linha 2 no meio de +---...---+ */ x = (g_ncols - 48) / 2; /* obtenha centro da tela */ puts("\n"); printf("%*s+%*.*s+\n", x, " ", TOTAL, TOTAL, buff); printf("%*s|%*s%s%-*s|\n", x, " ", centerl1, " ", szTitle[0], TOTAL-centerl1-nl1, " "); printf("%*s|%*s|\n", x, " ", TOTAL, " "); printf("%*s|%*s%s%-*s|\n", x, " ", centerl2, " ", szTitle[1], TOTAL-centerl2-nl2, " "); printf("%*s+%*.*s+\n\n\n", x, " ", TOTAL, TOTAL, buff); }/* Greeting */ /*! * Imprime opcoes do menu. */ void PrintMenu(void) { static const char *szMenu[] = { "\x03 1 - Cria fila.", "\x03 2 - Cadastro de estudante(s).", "\x03 3 - Imprime.", "\x07 31 - Fila.", "\x07 32 - Lista de estudantes.", "\x07 33 - Pilha de brindes.", "\x03 4 - Brinde (armazena na pilha).", "\x03 5 - Remover Cadastro.", "\x03 6 - Sair.", NULL }; const char **p; char buff[128]; #define SIZE_TAB 8 #undef TOTAL #define TOTAL 42 memset(buff, '-', 126); buff[127] = '\0'; printf("%*s+---Menu%.*s+\n", SIZE_TAB, " ", TOTAL-7, buff); printf("%*s|%*s|\n", SIZE_TAB, " ", TOTAL, " "); p = szMenu; while( *p ) { printf("%*s|%*s%s%*s|\n", SIZE_TAB, " ", **p, " ", (*p)+2, (int)(TOTAL - strlen((*p)+2) - **p), " "); p++; } printf("%*s|%*s|\n", SIZE_TAB, " ", TOTAL, " "); printf("%*s+%.*s+\n\n", SIZE_TAB, " ", TOTAL, buff); printf("%*s%s", SIZE_TAB, " ", "OPCAO: "); fflush(stdout); /* force impressao na tela */ }/* PrintMenu */ /*! * \brief Processa opcoes do usuario. * * \param [in] pOpt - ponteiro para string com a entrada do usuário. * \param [in] opt - opção do usuário na forma enum optMenu. * \param [in, out] pFila - ponteiro para a fila. * \param [in, out] pLista - ponteiro para a lista. * \param [in, out] pPilha - pilha de elementos; brindes. * * \return Uma opção do menu passado como comando.\n * -pf Imprime fila.\n * -pl Imprime lista.\n * -pp Imprime a pilha.\n * oNone (não faça nada) em caso nenhum comando foi encontrado. * * \remarks pOpt tera uma string truncada em caso usuario inseriu uma * entrada maior que o buffer. * * \sa CriaFila, InsereLista, Imprime, Brinde, Remove */ enum eOptMenu ParseOption(const char* pOpt, enum eOptMenu opt, struct TipoFila *pFila, struct TipoLista *pLista, struct TipoPilha *pPilha) { enum eOptMenu OptRet = oNone; switch(opt) { case oCreateQueue: OptRet = CriaFila(pFila); break; case oInsertStudent: OptRet = InsertStudent(pFila, pLista, pPilha); break; case oPrintQueue: ImprimeFila(pFila); WaitReturn(); break; case oPrintList: ImprimeLista(pLista); WaitReturn(); break; case oPrintStack: ImprimePilha(pPilha); WaitReturn(); break; case oGift: OptRet = InsertPilha(pPilha); break; case oRemove: OptRet = RemoveStudent(pLista); break; default: printf("'%s' opcao invalida.\n", pOpt); WaitReturn(); } return OptRet; }/* ParseOption */ /*! * \brief Remove aluno da lista duplamente encadeada. * * \param [in, out] pLista - in informações a respeito da lista. * out atualiza lista em caso algum registro foi removido. * * \return Uma opção do menu passado como comando.\n * -pf Imprime fila.\n * -pl Imprime lista.\n * -pp Imprime a pilha.\n * oNone (não faça nada) em caso nenhum comando foi encontrado. * * \sa VaziaLista, LogMessage, WaitReturn, PERRLN, WhichCmd, RemoveLista */ enum eOptMenu RemoveStudent(struct TipoLista *pLista) { static const char szErrMsg[] = "\tErro: nao e possivel remover, lista vazia.\n"; struct TipoElementoLista *aux; enum eOptMenu opt; int n; char *p; char DelName[TAM_NOME]; opt = oNone; LogMessage("Removendo aluno...\n"); if( VaziaLista(pLista) ) { fprintf(stderr, szErrMsg+1); LogMessage(szErrMsg); WaitReturn(); return opt; } memset(DelName, '\0', sizeof(DelName)); printf("Entre nome do aluno a ser removido: "); fflush(stdout); n = read(STDIN_FILENO, DelName, sizeof(DelName)-1); if( n == -1 ) { *DelName = '\0'; PERRLN("Erro 'RemoveStudent'"); LogMessage("\tErro 'RemoveStudent': %s\n", strerror(errno)); WaitReturn(); return opt; } if( (p = strchr(DelName, '\n')) != NULL ) *p = '\0'; if( g_bStdinConnectedTotty && MoreInput() ) EmptyStdIn(); /* * Verifique se existe um comando no meio da entrada. * se usuario responder sim, o aluno nao sera removido * e chamara o cmd, se cmd for valido, caso contrario * WhichCmd retorna oNone. */ if( (opt = WhichCmd(DelName, "Aluno '%s' ainda nao foi removido", DelName)) != oNone ) return opt; aux = pLista->pHeadNodo->pProx; while( aux != pLista->pHeadNodo && (strcasecmp(DelName, aux->Elemento.nome) != 0) ) aux = aux->pProx; if( aux == pLista->pHeadNodo ) { fprintf(stderr, "Erro, aluno '%s' nao encontrado.\n", DelName); LogMessage("\tErro, aluno '%s' nao encontrado.\n", DelName); WaitReturn(); }else { RemoveLista(aux, pLista); fprintf(stderr, "Aluno '%s' removido com sucesso.\n", DelName); LogMessage("\tAluno '%s' removido com sucesso.\n", DelName); WaitReturn(); } return opt; } /*! * \brief Gera uma fila, cada elemento da fila é um inteiro, * representando a senha do aluno na fila. * * \param Fila - Estrutura de dados do TipoFila. * * \return Uma opção do menu passado como comando.\n * -pf Imprime fila.\n * -pl Imprime lista.\n * -pp Imprime a pilha.\n * oNone (não faça nada) em caso nenhum comando foi encontrado. * * \sa LogMessage, InicFila, PERRLN, WaitReturn, WhichCmd, Enfileira */ enum eOptMenu CriaFila(struct TipoFila *Fila) { enum eOptMenu opt; TipoAluno i, tamanho; char buffer[7]; char *p; int n, err; LogMessage("Criando fila...\n"); InicFila(Fila); printf("Entre com a quantidade de alunos: "); fflush(stdout); memset(buffer, '\0', sizeof(buffer)); n = read(STDIN_FILENO, buffer,sizeof(buffer)-1); err = errno; if (n == -1){ PERRLN("Erro 'CriaFila'"); LogMessage("\tErro criando fila: %s.\n", strerror(err)); *buffer='\0'; WaitReturn(); return oNone; } if( (p = strchr(buffer, '\n')) != NULL) *p = '\0'; if( g_bStdinConnectedTotty && MoreInput() ) EmptyStdIn(); tamanho = atoi(buffer); /* * Verifique se existe um comando no meio da entrada. * se usuario responder sim, a fila nao sera criada * e chamara o cmd, se cmd for valido, caso contrario * WhichCmd retorna oNone. */ if( (opt = WhichCmd(buffer, "A fila com '%d' elemento(s) ainda nao foi criada", tamanho)) != oNone ) return opt; if( tamanho > TamMax-1 || tamanho < 1 ) { fprintf(stderr, "Erro: '%s' tamanho invalido.\n", buffer); LogMessage("Erro: '%s' tamanho invalido.\n", buffer); WaitReturn(); }else { for(i=1;i<=tamanho;i++) Enfileira(i,Fila); } LogMessage("\t%d elemento(s) criado(s) na fila com sucesso.\n", tamanho); return oNone; } /*! * \brief Insere elementos na pilha. * * \details Cada elemento da pilha é um brinde que será * distribuido aos alunos por ordem de chegada, * ou seja, a cada aluno inscrito recebe o brinde * do topo da pilha. * * \param [in, out] pPilha - Recebe topo da pilha e empilha * cada novo elemento. * * \return Uma opção do menu passado como comando.\n * -pf Imprime fila.\n * -pl Imprime lista.\n * -pp Imprime a pilha.\n * oNone (não faça nada) em caso nenhum comando foi encontrado. * * \sa LogMessage, PERRLN, WaitReturn, MoreInput, WhichCmd, Push */ enum eOptMenu InsertPilha(struct TipoPilha *pPilha) { static const char szErrMsg[] = "\tATENCAO! char's excedente(s) de '%s' foram truncados.\n"; char *p; int n, count, err; enum eOptMenu opt; TipoItem item[TAM_ELEMENTO]; LogMessage("Inserindo elemento(s) na pilha...\n"); opt = oNone; memset(item, '\0', sizeof(item)); printf("\nEntre nome do brinde: "); fflush(stdout); n = read(STDIN_FILENO, item, sizeof(item)-1); err = errno; if( n == -1 ) { PERRLN("Erro 'InsertPilha'"); LogMessage("\tErro 'InsertPilha': %s.\n", strerror(err)); *item = '\0'; WaitReturn(); return oNone; }else if( (p = strchr(item, '\n')) != NULL ) *p = '\0'; if( g_bStdinConnectedTotty && MoreInput() ) { fprintf(stderr, szErrMsg+1, item); LogMessage(szErrMsg, item); WaitReturn(); } count = 0; while( *item ) { /* * Verifique existe um comando no meio da entrada. * se usuario responder sim, o elemento nao sera empilhado * e chamara o cmd, se cmd for valido, caso contrario * WhichCmd retorna oNone. */ if( (opt = WhichCmd(item, "Elemento '%s' ainda nao foi colocado na pilha", item)) != oNone ) return opt; if( *item && Push(item, pPilha) ) count++; printf("\nEntre nome do brinde: "); fflush(stdout); n = read(STDIN_FILENO, item, sizeof(item)-1); err = errno; if( (p = strchr(item, '\n')) != NULL ) *p = '\0'; if( n == -1 ) { PERRLN("Erro 'InsertPilha'"); LogMessage("\tErro 'InsertPilha' elemento: '%s' nao pode ser empilhado: %s.\n", item, strerror(err)); WaitReturn(); *item = '\0'; } if( g_bStdinConnectedTotty && MoreInput() ) { fprintf(stderr, szErrMsg+1, item); LogMessage(szErrMsg, item); WaitReturn(); } }/* while */ LogMessage("\t%d elemento(s) inserido(s) na pilha com sucesso.\n", count); return opt; }/* InsertPilha */ /*! * \brief Insere aluno na lista. * * \details função controla quando deverá parar * de pedir entrada para o usuário. Ela para * quando:\n * 1. InsertNextStudent retorna diferente de oNone * (opção não faça nada). * 2. O nome do aluno for deixada em branco. * 3. Não tem mais alunos na fila. * * \param [in, out] pFila - ponteiro para fila. * \param [in, out] pLista - ponteiro para a lista onde serão * inseridos os alunos. * \param [in, out] pPilha - ponteiro par a pilha. * * \return Uma opção do menu passado como comando.\n * -pf Imprime fila.\n * -pl Imprime lista.\n * -pp Imprime a pilha. * * \remarks O usuário tem a opção de entrar o nome em branco para finalizar * aquela entrada daquela senha, porém esta senha será perdida. * * \sa LogMessage, VaziaFila, WaitReturn, Desinfileira, InsertNextStudent */ enum eOptMenu InsertStudent(struct TipoFila *pFila, struct TipoLista *pLista, struct TipoPilha *pPilha) { static const char szWarn[] = "\tNao ha aluno(s) na fila.\n"; struct TipoItem item; TipoAluno senha; enum eOptMenu opt; int count = 0; opt = oNone; LogMessage("Inserindo aluno(s) na lista...\n"); memset(item.nome, '\0', sizeof(item.nome)); /* Insira alunos na lista ate que o nome seja entrado em branco. */ if( VaziaFila(pFila) ) { fprintf(stderr, szWarn+1); LogMessage(szWarn); WaitReturn(); }else { do { senha = Desinfileira(pFila); printf("Aluno com senha '%d', insira dados...", senha); fflush(stdout); opt = InsertNextStudent(&item, pLista, pPilha); if( *item.nome ) count++; else LogMessage("\tAluno da fila com senha '%d', ignorado.\n", senha); }while( opt == oNone && *item.nome && !VaziaFila(pFila) ); }/* else */ LogMessage("\t%d aluno(s) inserido(s) na lista com sucesso.\n", count); return opt; }/* InsertStudent */ /*! * \brief Insere próximo estudante na lista. * * \details Toda a checagen é feito nesta função, como: verificar * se todas as disciplinas foram entradas corretamente, * ou se nome foi entradado em branco, se usuário inseriu * um comando. * * \param [in, out] pitem - item a ser inserido na lista, observe * que este parâmetro serve como retorno para quem chamou, * vazio significa uma flag. * \param [in, out] pLista - ponteiro para a lisata onde serão inseridos * os novos alunos. * \param [in, out] pPilha - ponteiro para a pilha, de onde serão * desenpilhados os brindes a cada inscrito. * * \return Uma opção do menu passado como comando.\n * -pf Imprime fila.\n * -pl Imprime lista.\n * -pp Imprime a pilha.\n * oNone (não faça nada) em caso nenhum comando foi encontrado. * * \sa PERRLN, LogMessage, WaitReturn, WhichCmd, MoreInput, EmptyStdIn, * GetDisciplina, VaziaPilha, Pop, InsereLista */ enum eOptMenu InsertNextStudent(struct TipoItem *pitem, struct TipoLista *pLista, struct TipoPilha *pPilha) { char input[21]; char in_tmp[5]; char *p; enum eOptMenu opt; int n, i, err; bool not_valid; printf("\nEntre nome do aluno: "); fflush(stdout); n = read(STDIN_FILENO, pitem->nome, sizeof(pitem->nome)-1); err = errno; if( n == -1 ) { PERRLN("Erro 'InsertNextStudent'"); LogMessage("\tErro 'InsertNextStudent': %s.\n", err); *pitem->nome = '\0'; WaitReturn(); } /* verifique se usuario terminou entrada com LF '\n' */ if( (p = strchr(pitem->nome, '\n')) != NULL ) *p = '\0'; /* * Verifique se usuario entrou mais dados que * o limite do buffer, se sim consuma o restante. * Isto deve ocorrer somente se stdin estiver connectado * a um terminal. */ if( g_bStdinConnectedTotty && MoreInput() ) EmptyStdIn(); /* * Verifique se existe um comando no meio da entrada. * se usuario responder sim, o aluno sera ignorado e * a senha perdida e chamara o cmd, se cmd for valido, * caso contrario WhichCmd retorna oNone. */ if( (opt = WhichCmd(pitem->nome, "Aluno '%s' ainda nao cadastrado", pitem->nome)) != oNone ) return opt; /* se usuario deixou em braco saia */ if( *pitem->nome == '\0' ) return oNone; printf("\nEntre disciplinas 1 2 3 4 5: "); fflush(stdout); memset(input, '\0', sizeof(input)); /* obtenha entrada do usuario. */ n = read(STDIN_FILENO, input, sizeof(input)-1); err = errno; if( n == -1 ){ /* verifique se read falhou. */ PERRLN("Erro 'InsertNextStudent'"); LogMessage("\tErro 'InsertNextStudent': %s.\n", strerror(err)); *input = '\0'; WaitReturn(); } if( (p = strchr(input, '\n')) != NULL ) *p = '\0'; if( g_bStdinConnectedTotty && MoreInput() ) EmptyStdIn(); /* set buffer para vazio */ memset(in_tmp, '\0', sizeof(in_tmp)); /* set disciplinas para nao validos valores */ memset(pitem->disciplinas, 0, sizeof(pitem->disciplinas)); p = input; /* aponte para primeira entrada */ /* * O for vai da primeira a ultima disciplina, * do garante que somente validos valores serao entrados. */ for( i=0; i < MAX_DISCIPLINAS; i++) { /* obtenha proxima entrada do buffer*/ pitem->disciplinas[i] = GetDisciplina(&p); /* force usuario entrar dado valido, em caso e invalido. */ do{ not_valid = (pitem->disciplinas[i] < MIN_DISCIPLINA || pitem->disciplinas[i] > MAX_DISCIPLINA); if( not_valid ) { printf("Disciplina '%d=%u' invalida, Entre novamente: ", i+1, pitem->disciplinas[i]); fflush(stdout); n = read(0 /* stdin */, in_tmp, sizeof(in_tmp)-1); err = errno; if( n == -1 ) { PERRLN("Erro 'InsertNextStudent'"); LogMessage("\tErro 'InsertNextStudent': %s.\n", strerror(err)); *input = '\0'; WaitReturn(); } if( (p = strchr(in_tmp, '\n')) != NULL ) *p = '\0'; if(g_bStdinConnectedTotty && MoreInput() ) EmptyStdIn(); pitem->disciplinas[i] = atoi(in_tmp); } }while( not_valid ); }/* for */ /* obtenha brinde da pilha. */ if( VaziaPilha(pPilha) ) { *pitem->brinde = '\0'; /* set vazio */ printf("Pilha de brindes vazia!\n"); /* informe usuario */ fflush(stdout); LogMessage("\tNao ha brindes na pilha. Aluno: '%s' s/ brinde.\n", pitem->nome); }else strncpy(pitem->brinde, Pop(pPilha), sizeof(pitem->brinde)-1); /* tudo ok, insira aluno na lista. */ if ( !InsereLista(pitem, pLista) ) WaitReturn(); return oNone; }/* InsertNextStudent */ /*! * \brief Imprime na tela espere e aguarda pela tecla enter. * * \remarks Esta função só executara as linhas abaixo em caso * a variável global g_bStdinConnectedTotty = TRUE, entrada padrão esta * conectada ao terminal do usuário. Se o usuário entrar qualquer dado antes * de enter ele(s) serão ignorados. * * \sa MoreInput, EmptyStdIn */ void WaitReturn(void) { if( g_bStdinConnectedTotty ) { if( MoreInput() ) EmptyStdIn(); printf("Pressione enter para continuar..."); fflush(stdout); EmptyStdIn(); /* espera por enter, qualquer dado entrado sera ignorado */ } }/* WaitReturn */ /*! * \brief Seleciona um dos comandos de impressão, * função recebe um número variável de parâmetros. * * \param [in, out] pstr - in string com a entrada do * usuário contendo ou não um comando. Se existe * um comando ele sera extraído, tudo que estiver * depois do comando será ignorado out. * \param [in] fmt - formato de uma mensagem que deve aparacer * para o usuário. Segue os mesmos critérios de printf. * \param ... um número variável de parâmetros, comforme fmt. * * \return Uma opção do menu passado como comando.\n * -pf Imprime fila.\n * -pl Imprime lista.\n * -pp Imprime a pilha.\n * oNone (não faça nada) em caso nenhum comando foi encontrado. * * \remarks ATENÇÃO! qualquer dado depois do comando será ignorado. * * \sa GetCommand, LogMessage, WaitReturn, PERRLN, MoreInput, EmptyStdIn */ enum eOptMenu WhichCmd(char *pstr, const char *fmt, ...) { static const char szQuestion[] = ". Deseja continuar assim mesmo? [s, n]: "; enum eOptMenu opt; char *buff; char szInput[4]; char foo; int size, err; va_list ap; memset(szInput, '\0', sizeof(szInput)); opt = GetCommand(pstr, szInput); /* * 1. pouco importa se pstr e vazio ou nao, se * opt = oUnknown informe usuario e retorne * nao faca nada. * 2. opt = oNone nao ha comando nenhum. Retorne * nao faca nada. * 3. Se GetCommand() retornou string vazia (em pstr), e * como casos 1. e 2. ja foram cobertos opt=cmdvalido, * logo retorne comando do usuario. * 4. caso ha dados a serem analizados e um comando valido. */ if( opt == oUnknown ) { fprintf(stderr, "Comando '%s' invalido.\n", szInput); LogMessage("Comando '%s' invalido.\n", szInput); WaitReturn(); return oNone; }else if( opt == oNone ) return oNone; else if( !*pstr ) return opt; /* * 4. Caso: * Se chegou nesta linha temos dados com o comando * informe usuario, e obtenha desicao */ /* obtenha tamanho do buffer necessario */ va_start(ap, fmt); size = vsnprintf(&foo, 1, fmt, ap) + 1; err = errno; va_end(ap); if( size < 1 ) { PERRLN("Erro 'ParseCmd'"); LogMessage("Erro 'ParseCmd': %s\n", strerror(err)); WaitReturn(); return oNone; } if( (buff = (char*)malloc(size+sizeof(szQuestion))) == NULL ) { err = errno; PERRLN("Erro 'ParseCmd'"); LogMessage("Erro 'ParseCmd': %s\n", strerror(err)); WaitReturn(); return oNone; } /* construa msg para informar usuario */ va_start(ap, fmt); size = vsnprintf(buff, size, fmt, ap); err = errno; va_end(ap); if( size < 1 ) { PERRLN("Erro 'ParseCmd'"); LogMessage("Erro 'ParseCmd': %s\n", strerror(err)); WaitReturn(); free(buff); return oNone; } strcat(buff+size-1, szQuestion); /* obtenha resposta do usuario */ do { printf("%s", buff); fflush(stdout); size = read(STDIN_FILENO, szInput, sizeof(szInput)-1); err = errno; if( size == -1 ) { PERRLN("Erro 'ParseCmd'"); LogMessage("Erro 'ParseCmd': %s\n", strerror(err)); WaitReturn(); /* force saida */ szInput[0] = 'n'; szInput[1] = '\n'; }else if( g_bStdinConnectedTotty && MoreInput() ) EmptyStdIn(); }while( (szInput[0] != 's' || szInput[1] != '\n') && (szInput[0] != 'n' || szInput[1] != '\n') ); /* * se usuario responder nao, retorne nao faca nada, * caso contrario retorne a opcao do usuario. */ if( *szInput == 'n' ) opt = oNone; free(buff); return opt; }/* ParseCmd() */ /*! * \brief Obtém disciplina através da string passada como parâmetro. * \details Faz o parse das disciplinas. * * \param [in, out] p - ponteiro para ponteiro. Este parâmetro * recebe o endereco do ponteiro da string que contém * as entradas das disciplinas. Este buffer é scaniado * até que seja encontrado um espaço, quando encontrado * é substituído por final da string, e convertido para * um inteiro. Como a função recebe o endereço da variável * ela é atualiza para a próxima entrada. ATENÇÃO! quem * chamou deve passar o endereço de uma variável que pode * perder seu conteúdo. * * \return Retorna um inteiro referente a disciplina que usuário entrou. * Em p é retornado o ponteiro para o primeiro char da próxima entrada, * ou final da entrada. * * \remarks A decisão de implementar desta maneira é para o código ficar * mais robusto, não correndo riscos de laco infinito ou falha * de segmentacao, por outras funcoes como scanf(). */ int GetDisciplina(char **p) { char *tmp = *p; int dis; /* encontre espaco ' ' 0x20 */ while( *tmp && *tmp != ' ' ) tmp++; if( *tmp == ' ' ) *tmp++ = '\0'; /* verifique se estamos no espaco, se sim atribua final da string e apponte para proxima entrada. */ dis = atoi(*p); /* * salve ponteiro para a proxima entrada. * isto deve ser feito depois de convertermos * a entrada para int, pois estamos usando o * mesmo ponteiro. */ *p = tmp; return dis; }/* GetDisciplina */ /*! * \brief Obtem um comando através de uma string. * * \param pstr - Ponteiro para uma string qualquer * contendo um dos comandos abaixo:\n * -pf - Imprime fila\n * -pl - ImprimeLista\n * -pp - ImprimePilha\n * \param pcmd - Ponteiro de retorno com o comando * extraido da string do parametro um. * * \return Uma opção do menu passado como comando.\n * -pf Imprime fila.\n * -pl Imprime lista.\n * -pp Imprime a pilha.\n * oNone (não faça nada) em caso nenhum comando foi encontrado. * * \remark Seguindo as especificações propostas esta função * possibilita ao usuário utilizar um comando * de impressão a qualquer momento de execução do programa. */ enum eOptMenu GetCommand(char *pstr, char *pcmd) { char *p = pstr; enum eOptMenu opt; /* assuma cmd desconhecido, evitamos uma comparacao. */ opt = oUnknown; /* procure pela flag the comando */ while( *p && *p != '-' ) p++; /* se nao identificado comando, retorne nao faca nada*/ if( !*p ) return oNone; /* extraia o comando */ if( *p == '-' && *(p+1) == 'p' ) { if ( *(p+2) == 'f') opt = oPrintQueue; else if ( *(p+2) == 'l') opt = oPrintList; else if ( *(p+2) == 'p') opt = oPrintStack; strncpy(pcmd, p, 3); }else *pcmd = '\0'; /* ignore qualquer string apartir do comando */ *p = '\0'; return opt; }/* GetCommand */