This commit was generated by cvs2svn to compensate for changes in r8625,

which included commits to RCS files with non-trunk default branches.

git-svn-id: svn://10.65.10.50/trunk@8626 c028cbd2-c16b-5b4b-a496-9718f37d4682
This commit is contained in:
luca 1999-10-20 15:27:08 +00:00
parent 8fa72f5d1a
commit 3dbdc66142
19 changed files with 5842 additions and 0 deletions

Binary file not shown.

135
webcgi/formazione/README Executable file
View File

@ -0,0 +1,135 @@
Software di gestione moduli didattici per la formazione a distanza ver. 0.1
==================================================================
Copyright Aga informatica 1999.
DISCLAIMER
----------
Le seguenti note sono solo indicative e vanno prese così come sono; ovvero
come promemoria. Quando avrò un poco più di tempo provvederò a farne una
stesura completa ed esauriente, con tanto di discussione della logica dei
programmi, per la gioia ed il gaudio di tutti. Per ora accontentatevi...
Teoricamente potrebbe essere possibile compilarlo anche sotto NT, basta avere
gcc. Al posto di postgres si dovrebbe adattare il sorgente creando un piccolo
middleware per utilizzare SQL server od un DBMS generico.
NOTE
----
Per l'installazione del software cambiare l'install directory presente nel
Makefile e quindi dare il comando make install come root.
Per l'installazione dei tracciati, dopo aver installato Postgres 6.3,
creare il database corsi:
createdb corsi
creare le tabelle:
psql -e corsi < tracciato
impostare eventuali opzioni di sicurezza/accessi esterni in pg_hba.conf e garantire l'accesso
all'utente nobody, o comunque a quello per mezzo del quale vengono eseguiti
i CGI dall'http server tramite lo statement SQL:
GRANT ALL ON <tabelle> TO PUBLIC;
La struttura dei direttori deve essere siffatta:
<Root directory HTTP>---/corsi/----/cgi-bin/
|
|-/corso_<x1>
|
|-/corso_<x2>
|
|-/corso_<x3>
Ogni direttorio del corso deve avere un direttorio documenti ed eventualmente
un direttorio upload. Tali direttori devono essere protetti in lettura, ovvero
non possono essere letti da chiunque se non dal gruppo appartenente al personale
di servizio.
Il direttorio cgi-bin invece, oltre ad essere configurato dal server http come
direttorio di esecuzione CGI, deve avere l'accesso possibile oltre che dal personale
di servizio, anche dai membri dei corsi.
In cgi-bin andrà anche installato il direttorio HyperNews debitamente configurato.
Ricordarsi di compilare per ogni utente anche il corso del quale fa parte. Tale
indicazione deve essere uguale ai vari direttori dei corsi <x1>, <x2> o <x3> etc.
All'interno del direttorio documenti vi sono tanti direttori quanti i moduli
validi per il corso; ognuno è contraddistinto dal suo numero registrato sul
database con una "M" davanti. All'interno di ognuno di questi direttori, M1,M2,
M3 etc, vi sarà la pagina iniziale (index.htm) di presentazione e riferimento
ai vari materiali. I materiali presenti in tale direttorio possono essere files
di qualsiasi tipo. Infine oltre ai materiali vi sono anche i test necessari
per l'ingresso e passaggio da una unità didattica all'altra: test_a.htm, test_b.htm
test_c.htm... test_z.htm, dove test_a.htm è quello obbligatorio per l'ingresso
e test_z.htm è quello per verificare la conoscenza finale del modulo.o
<Root directory HTTP>--/corsi/--/corso_<x1>/upload/
|
/documenti/
|
|-/M1/..index.htm, materiali, test
|
|-/M2/
|
|-/M3/
I riferimenti alle class-room per i singoli moduli devono avere lo stesso
nome col quale il modulo stesso viene registrato nel database. Fare attenzione
perche' la cosa e' case sensitive.
TODO
----
*) Possibilità di gestione utente tutore, in modo che possa accedere liberamente
ad ogni corso, ad ogni modulo, ad ogni classroom, ad ogni test senza memorizzarne
risultati etc. Questa modifica è collegata alla memorizzazione delle informazioni
relative ad ogni singolo corso. E' da decidere se ci vuole una tabella in più
(corsi, con nome e descrizione) e dove mettere le informazioni di appartenenza
al corso: sul modulo o sull'utente. Se si mettono sull'utente è necessario
trovare un modo, nel caso del tutor, affinchè si possa ricostruire il path
completo del modulo. Probabilmente si dovrà unificare tutti i direttori documenti
dei vari corsi, e tener distinti i moduli, visto che ognuno è cmq diverso.
In tal modo probabilmente si avrà un unico direttorio "documenti" sotto "corsi"
e non tanti ognuno sotto corso_<xxx>, facilitando la composizione del path del
file da reperire, siano essi tests o materiali. In effetti visto che i moduli
sono n, non vi è motivo di tenerli separati per corso. Per coerenza l'unica
cosa che si può implementare è un campo in più sulla tabella moduli nel
quale evidenziare di quale corso fa parte ma sarebbe una informazione ridondante.
*) Interfaccia NSAPI per Netscape Fasttrack, in modo che l'autenticazione sia effettuata
controllando il database utenti postgres. Così facendo si evitano duplicazioni
di user-ID. Ovvio che tale interfaccia NSAPI, sarà valida solo per Fasttrack
server. Nel caso di altro http server, ad esempio Apache, la cosa è da studiare.
*) Verificare le potenzialità di HyperNews ed eventualmente cercare una message board
più flessibile per i nostri scopi. Attualmente il sistema di autenticazione
manuale incapsulato all'interno non è un gran ché; fare in modo che utilizzi
quello del browser. In genere per HyperNews, basta impostare un unico utente,
il creatore dei forum ed amministratore del sito.
*) Implementare un miglior metodo per far sì che i client non mettano in cache
i risultati dei vari cgi. Attualmente l'header expires non funziona molto bene,
infatti quando si torna indietro col browser, dice sempre che il documento
è spirato. Analogamente se si decide di stampare una qualsiasi pagina. Forse
tramite i cookies...
*) Maggior parametrizzazione per l'installazione, in modo da fare un bell'RPM
cosicchè si possa vendere il prodotto!
*) Maggior documentazione... ma questo si sapeva già...
*) Se possibile trovare un modo per togliere i pulsanti di azione dalle pagine
di ogni modulo (quelli per scaricare le lezioni, accedere ai tests ecc) e
sostituirli con hyperlink. Il problema è che si tratta di forms che spediscono
col metodo POST. Forse implementando una riga unica con i paramateri necessari
ci si può riuscire.
--Angelo

56
webcgi/formazione/applicat.cpp Executable file
View File

@ -0,0 +1,56 @@
#include "applicat.h"
void Application::print_content()
{
cout << "Content-type: text/html" << endl ;
// Force expiring action, since these are CGI outputs...
// Guess this date...
cout << "Expires: Tue, 10 Nov 1992 00:00:00 GMT" << endl << endl;
}
void Application::print_bin_content()
{
cout << "Content-type: application/octet-stream" << endl << endl;
}
void Application::print_database_error()
{
cout << "<BR><BR><H1>Errore sul database</H1><BR><BR>" << endl;
cout << "<P>Connessione al database fallita.</P><BR>" << endl;
}
void Application::print_header(const char* title)
{
print_content();
cout << "<HTML>" << endl;
cout << "<HEAD>" << endl;
cout << "<TITLE>" << title << "</TITLE>" << endl;
cout << "<link rel=\"stylesheet\" href=\"/def_style.css\" type=\"text/css\">" << endl;
cout << "</HEAD>" << endl;
cout << "<BODY>" << endl;
}
void Application::print_footer()
{
cout << "<BR><BR>" << endl;
//cout << "Torna <A HREF=\"javascript:history.back(1)\">indietro</A>" << endl;
cout << "</BODY></HTML>" << endl;
}
void Application::run(int argc, char* argv[])
{
__argc = argc;
__argv = (const char**)argv;
if (create())
main_func();
destroy();
}

37
webcgi/formazione/applicat.h Executable file
View File

@ -0,0 +1,37 @@
// Libreria generica per applicazioni CGI della formazione a distanza
// Applicazione generica
////////////////////////
#include "defines.h"
class Application
{
int __argc;
const char** __argv;
protected:
virtual bool create() { return TRUE; }
virtual bool destroy() { return TRUE; }
virtual void main_func() {};
virtual void print_content();
virtual void print_bin_content();
virtual void print_database_error();
virtual void print_header(const char* title);
virtual void print_footer();
public:
int argc() const { return __argc; }
const char** argv() const { return __argv; }
const char* argv(int i) const { return __argv[i]; }
virtual void run(int argc, char* argv[]);
Application() {};
virtual ~Application() {};
};

View File

@ -0,0 +1,319 @@
// check_answers.cgi: applicazione per controllare i punteggi e memorizzare le risposte
// dei test.
#include <libpq++.h>
#include "applicat.h"
#include "questionnaire.h"
class Check_answers_Application : public Application
{
private:
String _dbname;
String _utente; // Utente corrente
String _modulo; // Numero del modulo del quale controllare le risposte
String _testnum; // Numero del test del quale controllare le risposte
PgEnv _environment;
bool _blank_test;
String _user_answers;
Questionnaire _questionario;
PgTransaction *_db;
protected:
virtual bool create();
virtual bool destroy();
virtual void main_func();
void check_answers();
void print_access_error();
bool load_corrector();
public:
Check_answers_Application() {_db = NULL;}
virtual ~Check_answers_Application() {};
};
bool Check_answers_Application::create()
{
String separator, ws;
String qs[3];
char answers[MAXQUESTIONS][MAXANSWERS];
for (int i=0; i<MAXQUESTIONS; i++)
for (int j=0; j<MAXANSWERS; j++)
answers[i][j] = 'F';
_user_answers = "";
separator = "_";
_dbname = POSTGRES_DB;
_environment.Port(POSTGRES_PORT);
_environment.Host(POSTGRES_HOST);
print_header("Controllo punteggi");
_utente = getenv("REMOTE_USER");
_blank_test = FALSE;
char *t1, *t2;
char *ccc = getenv("CONTENT_LENGTH");
int cl = ccc != NULL ? atoi(ccc) : 0;
#ifdef DBG
cout << "<BR>Lunghezza del buffer: " << cl << "<BR>" << endl;
#endif
if (cl < 8192) { // Evita di ricevere troppa roba sullo stdin (8kb sono abbastanza??)
for (int x = 0; cl && (!feof(stdin)); x++) {
t1 = fmakeword(stdin, '&', &cl);
t2 = makeword(t1, '=');
unescape_url(t1);
unescape_url(t2);
if (!strcmp(t2,"MODULO"))
_modulo = t1;
if (!strcmp(t2,"TESTNUM"))
_testnum = t1;
if (!strcmp(t2,"BLANK_TEST"))
_blank_test = !strcmp(t1,"ON");
// Carica le risposte fornite
if (t2[0] == 'Q') { // Si tratta di una risposta del test; formato Q_<numero_domanda>_<numero_risposta>
// Estrae il numero relativo alla domanda ed il numero della risposta
ws = t2;
const int e = split(ws, qs, 3, separator);
if (e == 3) {
const int qnum = atoi(qs[1]) - 1; // riferimento alla domanda
const int anum = atoi(qs[2]) - 1; // riferimento alla risposta
if (qnum < MAXQUESTIONS && anum < MAXANSWERS)
if (!strcmp(t1,"ON"))
answers[qnum][anum] = 'V';
}
}
}
// Compone _user_answers
for (int i=0; i<MAXQUESTIONS; i++) {
_user_answers += "Q_";
_user_answers += itoa(i+1);
_user_answers += "=";
for (int j=0; j<MAXANSWERS; j++)
_user_answers += answers[i][j];
_user_answers += ";";
}
// Debug stuff, useful in many wayz
#ifdef DBG
cout << "<H2>Risposte fornite</H2><BR><BR>" << endl;
for (int j = 0; j < MAXQUESTIONS; j++) {
cout << "Domanda " << j+1 << ":<BR>";
for (int k = 0; k < MAXANSWERS; k++)
if (answers[j][k] == 'V')
cout << "VERO ";
else
cout << "FALSO ";
cout << "<BR>" << endl;
}
return FALSE;
#endif
}
else {
cout << "<H2>Troppi dati inviati</H2><BR><BR>" << endl;
cout << "<P>L'applicazione ha ricevuto troppi dati sul buffer d'ingresso.</P><BR>" << endl;
return FALSE;
}
return TRUE;
}
bool Check_answers_Application::destroy()
{
print_footer();
return TRUE;
}
bool Check_answers_Application::load_corrector()
{
bool loaded = FALSE;
String command, correct_answers, separator;
separator = ";";
command = "SELECT * FROM CORRETTORI WHERE modulo=";
command += _modulo;
command += " AND testnum='";
command += _testnum;
command += "'";
_db->ExecCommandOk(command);
const int tuples = _db->Tuples();
if (tuples > 0) {
correct_answers = _db->GetValue(0, "risposte");
loaded = _questionario.load(correct_answers, _user_answers);
}
return loaded;
}
void Check_answers_Application::check_answers()
{
if (!load_corrector()) {
cout << "<H2>Correttore inesistente</H2><BR><BR>" << endl;
cout << "<P>Impossibile caricare il correttore per il test immesso.</P>";
cout << "<P>Rivolgersi al docente della lezione odierna.</P>";
return;
}
// 1 of theze dayz I'll fall to pieces anywayz
String command, verifiche, corso;
// Compone la stringa di selezione utente.
command = "SELECT * FROM UTENTI WHERE loginname='";
command += _utente;
command += "' AND logged='t'";
// Esegue il comando SQL, dovrebbe venir ritornata una sola tupla
// al limite se ne vengono ritornate piu' di una verranno ignorate
_db->ExecCommandOk(command);
const int tuples = _db->Tuples();
if (tuples > 0) {
// Aggiorna lo stato dei test effettuati per modulo sulla tabella UTENTI
verifiche = _db->GetValue(0, "verifiche");
corso = _db->GetValue(0, "course");
trim(corso);
const unsigned int mod_int = atoi(_modulo);
const unsigned int l = verifiche.length();
if ((l < mod_int || verifiche[mod_int-1] != _testnum[0]) && mod_int <= MAXMODULES) {
if (l< mod_int)
for (unsigned int j=l; j < mod_int; j++)
verifiche += ' ';
verifiche[mod_int-1] = _testnum[0];
command = "UPDATE UTENTI SET verifiche='";
command += verifiche;
command += "' WHERE loginname='";
command += _utente;
command += "'";
_db->ExecCommandOk(command);
// Controlla i punteggi inseriti nella matrice con quelli memorizzati nel correttore
// Compone le risposte fornite da memorizzare sulla tabella VERIFICHE in un formato più "umano"
Rational punteggio;
punteggio = _blank_test ? ZERO : _questionario.calc_score();
long progressivo = 0L;
// Blocca la tabella dei progressivi e prende il progressivo
command = "LOCK PROGRESSIVI";
_db->ExecCommandOk(command);
command = "SELECT * FROM PROGRESSIVI";
if (_db->ExecTuplesOk(command) && _db->Tuples() > 0){
progressivo = atol(_db->GetValue(0, "progverifiche"));
}
else { // Se non c'è nessuna riga viene aggiunta
command = "INSERT INTO PROGRESSIVI VALUES(0,0)";
_db->ExecCommandOk(command);
}
progressivo++;
command = "UPDATE PROGRESSIVI SET progverifiche=progverifiche+1"; // Incrementa il progressivo
_db->ExecCommandOk(command); // Aggiorna la tabella PROGRESSIVI
// Memorizza il punteggio e le risposte fornite sulla tabella VERIFICHE
command = "INSERT INTO VERIFICHE VALUES (";
command += ltoa(progressivo);
command += ",'";
command += _utente;
command += "',current_timestamp,";
command += _modulo;
command += ",'";
command += _testnum;
command += "','";
if (_blank_test)
command += " " ;
else
command += _user_answers; // Risposte fornite in formato "umano"
command += "',";
command += dtoa((double) punteggio); // Punteggio sottoforma di double
command += ")";
_db->ExecCommandOk(command);
cout << "<H2>Risultato del test</H2><BR><BR>" << endl;
if (!_blank_test)
_questionario.dump_html(corso, atoi(_modulo), _testnum[0]);
else
cout << "<P>Test consegnato <STRONG> in bianco </STRONG><BR><BR><BR>";
cout << "<TABLE><TR><TD><FORM METHOD=\"POST\" ACTION=";
cout << GET_MODULE_CGI << ">";
cout << "Cliccate sull'icona qui sotto per ritornare all'area formativa del modulo.";
cout << "<BR><BR><INPUT TYPE=HIDDEN NAME=\"MODULO\" VALUE=\"" << _modulo << "\">";
cout << "<INPUT TYPE=\"IMAGE\" SRC=\"/images/area_" << mod_int;
cout << ".gif\" ALIGN=top ALT=\"Accesso al modulo\" BORDER=0>";
cout << "</FORM></TD></TR></TABLE></P>" << endl;
}
else { // Se il test è già stato eseguito, nega l'aggiornamento il calcolo punteggio e tutto il resto
cout << "<H2>Test già eseguito</H2><BR><BR>" << endl;
cout << "<P>Il test selezionato risulta già eseguito.</P>";
cout << "<P>Si prega di riselezionare l'area corretta dalla pagina di <A HREF=\"";
cout << LOGIN_CGI << "\">selezione moduli.</A></P>";
}
}
else { // Se non trova i dati dell'utente indicato
// visualizza la mancanza di informazioni per completare la pagina.
cout << "<H2>Mancanza di informazioni</H2><BR><BR>" << endl;
cout << "<P>Impossibile reperire le informazioni relative al percorso formativo dell'utente " << _utente;
cout << ".</P><P>Controllare l'esattezza delle informazioni inserite.</P>";
}
return;
}
void Check_answers_Application::print_access_error()
{
cout << "<H2>Errore di accesso</H2><BR><BR>";
cout << "<P>Utilizzare la normale procedura di <A HREF=\"";
cout << LOGIN_CGI << "\">accesso</A> ai corsi.</P><BR>";
}
void Check_answers_Application::main_func()
{
// Controllo utente: se il CGI viene chiamato senza tutte le indicazioni necessarie
// visualizza l'errore
if (_utente.empty() || _modulo.empty() || _testnum.empty() || _user_answers.empty()) {
print_access_error();
return;
}
// Se tutto è OK...
// Inizia la transazione
_db = new PgTransaction(_environment, _dbname);
if ( _db->ConnectionBad() )
print_database_error();
else
check_answers();
delete _db; // Termina la transazione
return;
}
int main(int argc, char* argv[])
{
Check_answers_Application* a = new Check_answers_Application();
a->run(argc, argv);
delete a;
exit(0);
}

View File

@ -0,0 +1,76 @@
#!/usr/bin/perl -T
$dbname = 'corsi';
$dbhost = 'localhost';
use Pg;
if (check_user("utente_fad", "Excel-base") eq 0) {
print "Can't find it\n";
}
else {
print "Found it\n";
}
print get_course("utente_fad");
sub get_course {
my $retval;
my $loginname=$_[0];
$retval = "no_course";
# Compose query string and connect
$commandos = "SELECT * FROM UTENTI WHERE loginname='";
$commandos = "$commandos$loginname'";
$conn = Pg::connectdb("dbname=$dbname host=$dbhost");
if ($conn->status == PGRES_CONNECTION_OK) {
$result = $conn->exec($commandos);
# Did you find anyone?
if ($result->ntuples > 0) {
$retval = $result->getvalue(0,$result->fnumber("course"));
}
}
$retval =~ tr/ //d;
$retval;
}
sub check_user {
my $commandos;
my $modules;
my $ret_val;
my $loginname= $_[0];
my $modulename= $_[1];
$ret_val = 0;
# Compose query string and connect
$commandos = "SELECT * FROM UTENTI WHERE loginname='";
$commandos = "$commandos$loginname'";
$conn = Pg::connectdb("dbname=$dbname host=$dbhost");
if ($conn->status == PGRES_CONNECTION_OK) {
$result = $conn->exec($commandos);
# Did you find anyone?
if ($result->ntuples > 0) {
$modules = $result->getvalue(0,$result->fnumber("modules"));
$commandos = "SELECT * FROM MODULI WHERE modulename='";
# Search for $module_name and fetch its number
$commandos = "$commandos$modulename'";
$result = $conn->exec($commandos);
if ($result->ntuples > 0) {
$mod_num = $result->getvalue(0,$result->fnumber("modulenum"));
# Now check if $mod_num is enabled for this $loginname
$ret_val = 1 if (substr($modules, $mod_num-1,1) eq "X");
}
}
}
$ret_val;
}

52
webcgi/formazione/defines.h Executable file
View File

@ -0,0 +1,52 @@
#ifndef _DEFINES_H
#define _DEFINES_H
#include <fstream.h>
#include <bool.h>
#include <String.h>
#include <Rational.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#define POSTGRES_HOST "localhost"
#define POSTGRES_PORT ""
#define POSTGRES_DB "corsi"
#define FAD_DIR "/corsi/"
#define FAD_ROOT "/disk2/html/corsi/corso_"
#define FAD_CGI FAD_DIR"cgi-bin/"
#define GET_MODULE_CGI FAD_CGI"get_module.cgi"
#define LOGIN_CGI FAD_CGI"login.cgi"
#define LOGOUT_CGI FAD_CGI"logout.cgi"
#define CHECK_ANSWERS_CGI FAD_CGI"check_answers.cgi"
#define MAXMODULES 64
#define MAXQUESTIONS 20
#define MAXANSWERS 5
#define ERROR_FILE "__error" // File fittizio per segnalare gli errori
static Rational ZERO(0);
#ifdef __cplusplus
extern "C" {
#endif
void getword(char *word, char *line, char stop);
char *makeword(char *line, char stop);
char *fmakeword(FILE *f, char stop, int *cl);
char x2c(char *what);
void unescape_url(char *url);
void plustospace(char *str);
int rind(char *s, char c);
int getline(char *s, int n, FILE *f);
void send_fd(FILE *f, FILE *fd);
off_t fsize(const char* n);
char * itoa(int);
char * ltoa(long);
char * dtoa(double);
void trim(String& s);
#ifdef __cplusplus
}
#endif
#endif

319
webcgi/formazione/get_module.cpp Executable file
View File

@ -0,0 +1,319 @@
// get_module.cgi: applicazione per reperire la pagina (costruita dal docente)
// relativa al modulo passato. Tale pagina rimane fuori dall'albero del sito WWW
// Serve inoltre per ritornare le lezioni da scaricare ed i test da eseguire.
// Il comportamento del programma viene determinato in base alla presenza o meno
// di variabili passate sullo stdin:
// - se è presente solo la variabile MODULO allora introduce nella pagina iniziale del modulo
// - se sono presenti sia la variabile MODULO che TESTNUM allora ritorna il test indicato per tale modulo (eseguendo controlli se il test è già stato eseguito
// - se sono presenti MODULO TESTNUM e LEZIONE ritorna la lezione indicata, controllando il livello di test necessario per scaricarla
#include <libpq++.h>
#include "applicat.h"
enum behaviour { normal, get_test, get_lesson };
class Get_module_Application : public Application
{
private:
String _dbname;
String _utente; // Utente corrente
String _modulo; // Modulo
String _testnum; // Livello del test
String _lezione; // Nome della lezione
PgEnv _environment;
PgTransaction *_db;
behaviour _what; // Comportamento: reperisce l'indice del modulo, i test o le lezioni
protected:
virtual bool create();
virtual bool destroy();
virtual void main_func();
virtual void print_header(const char* title);
void print_access_error();
void get_module();
public:
Get_module_Application() {_db = NULL;}
virtual ~Get_module_Application() {};
};
bool Get_module_Application::create()
{
_dbname = POSTGRES_DB;
_environment.Port(POSTGRES_PORT);
_environment.Host(POSTGRES_HOST);
_utente = getenv("REMOTE_USER");
_modulo = "";
_what = normal;
char *t1, *t2;
char *ccc = getenv("CONTENT_LENGTH");
int cl = ccc != NULL ? atoi(ccc) : 0;
if (cl < 512) { // Evita di ricevere troppa roba sullo stdin
for (int x = 0; cl && (!feof(stdin)); x++) {
t1 = fmakeword(stdin, '&', &cl);
t2 = makeword(t1, '=');
unescape_url(t1);
unescape_url(t2);
if (!strcmp(t2,"MODULO"))
_modulo = t1;
if (!strcmp(t2,"TESTNUM"))
_testnum = t1;
if (!strcmp(t2,"LEZIONE"))
_lezione = t1;
}
if (!_testnum.empty())
_what = get_test;
if (!_lezione.empty())
_what = get_lesson;
}
else {
print_header("Troppi dati inviati");
cout << "<H2>Troppi dati inviati</H2><BR><BR>" << endl;
cout << "<P>L'applicazione ha ricevuto troppi dati sul buffer d'ingresso.</P><BR>" << endl;
print_footer();
return FALSE;
}
return TRUE;
}
bool Get_module_Application::destroy()
{
return TRUE;
}
void Get_module_Application::print_header(const char * title)
{
print_content();
cout << "<HTML>" << endl;
cout << "<HEAD>" << endl;
cout << "<TITLE>" << title << "</TITLE>" << endl;
cout << "<link rel=\"stylesheet\" href=\"/def_style.css\" type=\"text/css\">" << endl;
cout << "</HEAD>" << endl;
cout << "<BODY>" << endl;
}
void Get_module_Application::print_access_error()
{
print_header("Errore di accesso");
cout << "<H2>Errore di accesso</H2><BR><BR>";
cout << "<P>Utilizzare la normale procedura di <A HREF=\"";
cout << LOGIN_CGI << "\">accesso</A> ai corsi.</P><BR>";
print_footer();
return;
}
void Get_module_Application::get_module()
{
// Another one bites the dust...
String command;
// Compone la stringa di selezione utente: nome utente & logged
command = "SELECT * FROM UTENTI WHERE loginname='";
command += _utente;
command += "' AND logged='t'";
_db->ExecCommandOk(command);
const int tuples = _db->Tuples();
if (tuples > 0) { // E' loggato oppure no...
// Cosa deve fare:
// Verifica che il modulo passato sia effettivamente attivo per l'utente indicato
String moduli;
String corso;
moduli = _db->GetValue(0, "modules");
corso = _db->GetValue(0, "course");
trim(corso);
const unsigned int int_mod = atoi(_modulo)-1;
if (moduli.length() >= int_mod && moduli[int_mod] == 'X' && int_mod <= MAXMODULES -1) {
// Se si tratta del primo accesso al modulo, spedisce il questionario di inizio modulo
// Legge da FAD_ROOT il documento indicato da _modulo e lo restituisce sullo stdout così com'è
String filename, base_path;
FILE * html_page;
// La pagina base di ogni modulo sarà in FAD_ROOT/M<modulo>/index.htm
// Il test di inizio modulo sarà in FAD_ROOT/M<modulo>/test_a.htm
const char * sv = _db->GetValue(0, "verifiche");
const char level = strlen(sv) > int_mod ? sv[int_mod] : ' ';
const bool first_test = (level == ' ');
base_path = FAD_ROOT;
base_path += corso;
base_path += "/documenti/M";
base_path += _modulo;
base_path += "/";
filename = base_path;
switch (_what) {
case normal:
if (first_test) {
command = "SELECT * FROM MODULI WHERE modulenum=";
command += _modulo;
_db->ExecCommandOk(command);
bool faked = 0;
if (_db->Tuples() > 0)
faked = _db->GetValue(0,"faked")[0] == 't';
if (faked)
filename += "index.htm";
else
filename += "test_a.htm";
}
else
filename += "index.htm";
break;
case get_test:
if (level+1 == _testnum[0] || _testnum == "z") { // Caso speciale per l'ultimo test (z)
filename += "test_";
filename += _testnum;
filename += ".htm";
// Last test Handling: controlla qual'è l'ultimo test possibile per questo modulo
if (_testnum == "z") {
command = "SELECT * FROM MODULI WHERE modulenum=";
command += _modulo;
_db->ExecCommandOk(command);
if (_db->Tuples() > 0) { // Modulo presente?
const char * last_test = _db->GetValue(0,"lasttest");
if (last_test[0] != level) // Se non ha raggiunto l'ultimo test possibile, nega l'accesso all'ultimo test
filename = ERROR_FILE;
}
else
filename = ERROR_FILE;
}
}
else
filename = ERROR_FILE; // File inesistente per segnalare l'errore di livello sul test richiesto
break;
case get_lesson: // Reperisce la lezione indicata
if (level >= _testnum[0]) {
filename += _lezione;
}
else
filename = ERROR_FILE;
break;
}
html_page = fopen(filename, "r");
if (html_page != NULL) {
// Save some system resources, since sending the lessons may take some time
// So it seems to be quite reasonable freeing semaphores and shared memory allocated by Postgres connections.
delete _db;
_db = NULL;
const off_t sz = fsize(filename);
if (sz > 0)
cout << "Content-length: " << sz << endl;
if (_what == get_lesson) {
print_bin_content(); // Necessario per inviare files binari
}
else
print_content(); // Necessario per i files testo/html
send_fd(html_page, stdout);
fclose(html_page);
}
else { // Se non trova la risorsa da inviare, visualizza l'errore
switch (_what) {
case normal:
print_header(first_test ? "Questionario iniziale non implementato" : "Modulo non implementato");
if (first_test){
cout << "<H2>Questionario iniziale non implementato</H2><BR><BR>" << endl;
cout << "<P>Il questionario iniziale per il modulo selezionato non è presente.</P><BR>";
}
else {
cout << "<H2>Modulo non implementato</H2><BR><BR>" << endl;
cout << "<P>I materiali didattici per il modulo selezionato non sono presenti.</P><BR>";
}
break;
case get_test:
if (filename == ERROR_FILE) {
print_header("Accesso al livello di test non consentito");
cout << "<H2>Accesso al livello di test non consentito</H2><BR><BR>" << endl;
cout << "<P>L'utente <STRONG>" << _utente << "</STRONG> non ha i permessi necessari per accedere al livello di test richiesto oppure il test è già stato svolto.</P><BR>" << endl;
}
else {
print_header("Questionario non ancora implementato");
cout << "<H2>Questionario non ancora implementato</H2><BR><BR>" << endl;
cout << "<P>Il questionario per il modulo selezionato non è presente.</P><BR>";
}
break;
case get_lesson:
if (filename == ERROR_FILE) {
print_header("Accesso alla lezione non consentito");
cout << "<H2>Accesso alla lezione non consentito</H2><BR><BR>" << endl;
cout << "<P>L'utente <STRONG>" << _utente << "</STRONG> non ha i permesso necessari per accedere al livello di lezione richiesto.</P><BR>" << endl;
}
else {
print_header("Lezione non ancora implementata");
cout << "<H2>Lezione non ancora implementata</H2><BR><BR>" << endl;
cout << "<P>La lezione per il modulo selezionato non è presente.</P><BR>";
}
break;
}
print_footer();
}
}
else {
print_header("Accesso al modulo non consentito");
cout << "<H2>Accesso al modulo non consentito</H2><BR><BR>" << endl;
cout << "<P>L'utente <STRONG>"<< _utente << "</STRONG> non ha i permessi necessari per accedere al modulo indicato.</P><BR>";
print_footer();
}
}
else {
// Se non trova l'utente indicato oppure non era loggato
// visualizza ugualmente un messaggio ingannatore
print_access_error();
}
return;
}
void Get_module_Application::main_func()
{
// Controllo utente: se il CGI viene chiamato senza indicazioni d'utente e/o modulo
// rimanda al normale login
if (_utente.empty() || _modulo.empty()) {
print_access_error();
return;
}
// Se e' stato impostato l'utente, prende il documento indicato dal numero di modulo,
// non prima di aver controllato che l'utente sia loggato!
// Inizia la transazione
_db = new PgTransaction(_environment, _dbname);
if ( _db->ConnectionBad() ) {
print_header("Errore sul database");
print_database_error();
print_footer();
}
else
get_module();
if (_db != NULL)
delete _db; // Termina la transazione
return;
}
int main(int argc, char* argv[])
{
Get_module_Application* a = new Get_module_Application();
a->run(argc, argv);
delete a;
exit(0);
}

View File

@ -0,0 +1,72 @@
// Applicazione di prova per la classe Questionnaire
#include "applicat.h"
#include "questionnaire.h"
class Parsing_Application : public Application
{
private:
protected:
virtual bool create();
virtual bool destroy();
virtual void main_func();
public:
Parsing_Application() {}
virtual ~Parsing_Application() {};
};
bool Parsing_Application::create()
{
return TRUE;
}
bool Parsing_Application::destroy()
{
return TRUE;
}
void Parsing_Application::main_func()
{
Questionnaire q;
String s1,s2,s3;
s1 = "Q_1=VVVF;Q_2=FVFV;Q_3=VVVV;Q_4=VVFF;Q_5=VFFF;";
s2 = "Q_1=VVFFF;Q_2=FFFVF;Q_3=VVVVF;Q_4=VVFFF;Q_5=VFFFF;";
s3 = "volare";
q.load(s1,s2);
q.dump_html(s3, 1, 'a');
}
int main(int argc, char* argv[])
{
Parsing_Application* a = new Parsing_Application();
a->run(argc, argv);
delete a;
exit(0);
}

193
webcgi/formazione/login.cpp Executable file
View File

@ -0,0 +1,193 @@
// login.cgi: applicazione per registrare l'ingresso nell'area
// dei corsi. Modifica il template con i dati personali
// dell'utente (percorso formativo con moduli disponibili)
#include <libpq++.h>
#include "applicat.h"
class Login_Application : public Application
{
private:
String _dbname;
String _utente; // Utente corrente
String _realname;
String _moduli; // Moduli attivi per l'utente corrente
PgEnv _environment;
PgTransaction *_db;
protected:
virtual bool create();
virtual bool destroy();
virtual void main_func();
void login();
void print_access_error();
public:
Login_Application() {_db = NULL;}
virtual ~Login_Application() {};
};
void Login_Application::print_access_error()
{
cout << "<CENTER><H2>Accesso al corso</H2><BR><BR>" << endl;
cout << "<P>Si prega di effettuare regolarmente l'accesso al corso, fornendo le indicazioni<BR>";
cout << "di <B><I>utente</I></B> e <B><I>password</I></B> in vostro possesso.</P><BR>" << endl;
}
bool Login_Application::create()
{
_dbname = POSTGRES_DB;
_environment.Port(POSTGRES_PORT);
_environment.Host(POSTGRES_HOST);
print_header("Accesso all'area corsi");
_utente = getenv("REMOTE_USER");
return TRUE;
}
bool Login_Application::destroy()
{
print_footer();
return TRUE;
}
void Login_Application::login()
{
// Controlla se l'utente fornito è' un utente valido
String command;
// Compone la stringa di selezione utente.
command = "SELECT * FROM UTENTI WHERE loginname='";
command += _utente;
command += "'";
// Esegue il comando SQL, dovrebbe venir ritornata una sola tupla
// al limite se ne vengono ritornate piu' di una verranno ignorate
_db->ExecCommandOk(command);
const int tuples = _db->Tuples();
if (tuples > 0) {
// Cosa deve fare:
// Sistema le informazioni di logging dell'utente (anche sulla tabella ACCESSI)
// Carica il percorso formativo (campo modules) e compone la pagina
const bool logged = (_db->GetValue(0, "logged"))[0] == 't';
_realname = _db->GetValue(0, "realname");
_moduli = _db->GetValue(0, "modules");
// Aggiorna le informazioni di logging
if (!logged){
// Prende il progressivo per il logging sulla tabella accessi
long progressivo = 0L;
command = "LOCK PROGRESSIVI"; // Blocca la tabell all'interno di questa transazione per evitare sovrapposizioni
_db->ExecCommandOk(command); // Verrà automaticamente sbloccata al termine della transazione
command = "SELECT * FROM PROGRESSIVI"; // In caso il CGI si pianti la transazione viene abortita dal server, e di fatto non verrà fatto nessun aggiornamento alle tabelle
if (_db->ExecTuplesOk(command) && _db->Tuples() > 0){
progressivo = atol(_db->GetValue(0, "progaccessi"));
}
else { // Se non c'è nessuna riga viene aggiunta
command = "INSERT INTO PROGRESSIVI VALUES(0,0)";
_db->ExecCommandOk(command);
}
progressivo++;
command = "UPDATE PROGRESSIVI SET progaccessi=progaccessi+1"; // Incrementa il progressivo
_db->ExecCommandOk(command); // Aggiorna la tabella relativa
command = "UPDATE UTENTI SET logged=1, logindate=current_timestamp, ";
command += "progaccesso=";
command += ltoa(progressivo);
command += " WHERE loginname='";
command += _utente;
command += "'";
_db->ExecCommandOk(command); // Aggiorna la tabella utenti
command = "INSERT INTO ACCESSI VALUES (";
command += ltoa(progressivo);
command += ",'";
command += _utente;
command += "',current_timestamp, null)";
_db->ExecCommandOk(command); // Aggiunge un record alla tabella accessi
}
// Seleziona i moduli abilitati
command = "SELECT * FROM MODULI ORDER BY modulenum";
_db->ExecCommandOk(command);
cout << "<H2>Percorso formativo</H2><BR><BR>" << endl;
cout << "<P>Benvenuto/a, <STRONG>" << _realname << "</STRONG>.</P>" << endl;
cout << "<P>Ecco l'elenco dei moduli disponibili, clicca sull'immagine relativa al modulo per entrare nell'area di formazione prescelta. Si ricorda che i moduli contraddistinti da una piccola spirale fanno parte delle lezioni avanzate.</P>" << endl;
const int nt = _db->Tuples();
if (nt > 0) {
// Seleziona i moduli abilitati per costui
cout << "<BR><CENTER><TABLE WIDTH=50% CELLPADDING=4>" << endl;
int curr_module=0;
for (int i=0; i<nt;i++) {
const unsigned int mod_num = atoi(_db->GetValue(i, "modulenum"))-1;
if (_moduli.length() > mod_num+1 && _moduli[mod_num] == 'X') { // Se il modulo e' abilitato visualizza
const bool new_row = curr_module % 2 == 0;
curr_module++;
if (new_row)
cout << "<TR>" << endl;
cout << "<TD><FORM METHOD=\"POST\" ACTION=";
cout << GET_MODULE_CGI << ">";
cout << "<INPUT TYPE=HIDDEN NAME=\"MODULO\" VALUE=\"" << mod_num + 1 << "\">";
cout << "<INPUT TYPE=\"IMAGE\" SRC=\"/images/area_";
cout << mod_num+1 << ".gif\" ALIGN=top ALT=\"Accesso al modulo ";
cout << _db->GetValue(i, "modulename") << "\" BORDER=0>";
cout << "</FORM></TD>" << endl;
if (!new_row || i == nt - 1) // Se non è nuova riga o è l'ultimo elemento
cout << "</TR>" << endl;
}
}
cout << "</TABLE></CENTER>" << endl;
}
cout << "<BR><BR>" << endl;
}
else { // Se non trova i dati dell'utente indicato (con il percorso formativo)
// visualizza la mancanza di informazioni per completare la pagina.
cout << "<H2>Mancanza di informazioni</H2><BR><BR>" << endl;
cout << "<P>Impossibile reperire le informazioni relative al percorso formativo dell'utente <STRONG>" << _utente << "</STRONG>.</P>";
cout << "<P>Controllare l'esattezza delle informazioni inserite.</P><BR>";
}
return;
}
void Login_Application::main_func()
{
// Controllo utente: se il CGI viene chiamato senza indicazioni d'utente (impossibile o quasi)
// Allora mostra un avvertimento
if (_utente.empty()) {
print_access_error();
return;
}
// Se e' stato impostato l'utente, effettua la login()
// Inizia la transazione
_db = new PgTransaction(_environment, _dbname);
if ( _db->ConnectionBad() )
print_database_error();
else
login();
delete _db; // Termina la transazione
return;
}
int main(int argc, char* argv[])
{
Login_Application* a = new Login_Application();
a->run(argc, argv);
delete a;
exit(0);
}

127
webcgi/formazione/logout.cpp Executable file
View File

@ -0,0 +1,127 @@
// logout.cgi: applicazione per registrare l'uscita dall'area
// dei corsi.
#include <libpq++.h>
#include "applicat.h"
class Logout_Application : public Application
{
private:
String _dbname;
String _utente; // Utente corrente
PgEnv _environment;
PgTransaction *_db;
protected:
virtual bool create();
virtual bool destroy();
virtual void main_func();
void logout();
public:
Logout_Application() {_db = NULL;}
virtual ~Logout_Application() {};
};
bool Logout_Application::create()
{
_dbname = POSTGRES_DB;
_environment.Port(POSTGRES_PORT);
_environment.Host(POSTGRES_HOST);
print_header("Uscita dall'area corsi");
_utente = getenv("REMOTE_USER");;
return TRUE;
}
bool Logout_Application::destroy()
{
print_footer();
return TRUE;
}
void Logout_Application::logout()
{
//
String command;
// Compone la stringa di selezione utente: nome utente & logged altrimenti nisba logout!
command = "SELECT * FROM UTENTI WHERE loginname='";
command += _utente;
command += "' AND logged='t'";
_db->ExecCommandOk(command);
const int tuples = _db->Tuples();
if (tuples > 0) { // E' loggato oppure no...
// Cosa deve fare:
// Sistema le informazioni di logging dell'utente (anche sulla tabella ACCESSI)
String progressivo;
progressivo = _db->GetValue(0, "progaccesso");
command = "UPDATE UTENTI SET logged=0, logindate=null ";
command += " WHERE loginname='";
command += _utente;
command += "'";
_db->ExecCommandOk(command); // Aggiorna la tabella utenti
command = "UPDATE ACCESSI SET logoutdate=current_timestamp WHERE loginname='";
command += _utente;
command += "' AND progressivo=";
command += progressivo;
_db->ExecCommandOk(command); // Aggiorna la tabella accessi
}
// Se non trova l'utente indicato oppure non era loggato
// visualizza ugualmente un messaggio ingannatoreà
cout << "<H2>Uscita dall'area corsi</H2><BR><BR>" << endl;
cout << "<P>Uscita dall'area corsi effettuata con successo.</P><BR>";
cout << "<P>Le informazioni necessarie sono state registrate.</P><BR>";
cout << "<P><B>Grazie.</B></P><BR>";
return;
}
void Logout_Application::main_func()
{
// Controllo utente: se il CGI viene chiamato senza indicazioni d'utente
// non mostra proprio nulla (null logout)
if (_utente.empty()) {
cout << "<H2>Uscita dall'area corsi</H2><BR><BR>" << endl;
cout << "<P>Uscita dall'area corsi effettuata con successo.</P><BR>";
return;
}
// Se e' stato impostato l'utente, effettua la logout()
// Inizia la transazione
_db = new PgTransaction(_environment, _dbname);
if ( _db->ConnectionBad() )
print_database_error();
else
logout();
delete _db; // Termina la transazione
return;
}
int main(int argc, char* argv[])
{
Logout_Application* a = new Logout_Application();
a->run(argc, argv);
delete a;
exit(0);
}

73
webcgi/formazione/makefile Executable file
View File

@ -0,0 +1,73 @@
# Makefile for compiling CGI-FAD software
# Decommentare il seguente per includere le informazioni di debug
#CFLAGS=-Wall -g
# Usare il seguente per non includere le informazioni di debug
CFLAGS=-Wall -O2 -fomit-frame-pointer
PGINCLUDE=/usr/local/pgsql/include
PGLIB=/usr/local/pgsql/lib
INSTALLDIR=/disk2/html/corsi/cgi-bin
all: login.cgi logout.cgi get_module.cgi check_answers.cgi report.cgi html_parser score_corrector
install: all
install -m 750 -o nobody -g nobody -s login.cgi $(INSTALLDIR)
install -m 750 -o nobody -g nobody -s logout.cgi $(INSTALLDIR)
install -m 750 -o nobody -g nobody -s get_module.cgi $(INSTALLDIR)
install -m 750 -o nobody -g nobody -s check_answers.cgi $(INSTALLDIR)
install -m 750 -o nobody -g nobody -s report.cgi $(INSTALLDIR)
util.o: util.cpp
gcc $(CFLAGS) -c util.cpp
applicat.o: applicat.cpp applicat.h
g++ $(CFLAGS) -c applicat.cpp
login.o: login.cpp applicat.h
g++ $(CFLAGS) -c login.cpp -I$(PGINCLUDE)
login.cgi: login.o applicat.o util.o
g++ $(CFLAGS) -o login.cgi login.o applicat.o util.o -lpq -lpq++ -L$(PGLIB)
logout.o: logout.cpp applicat.h
g++ $(CFLAGS) -c logout.cpp -I$(PGINCLUDE)
logout.cgi: logout.o applicat.o util.o
g++ $(CFLAGS) -o logout.cgi logout.o applicat.o util.o -lpq -lpq++ -L$(PGLIB)
get_module.o: get_module.cpp applicat.h
g++ $(CFLAGS) -c get_module.cpp -I$(PGINCLUDE)
get_module.cgi: get_module.o applicat.o util.o
g++ $(CFLAGS) -o get_module.cgi get_module.o applicat.o util.o -lpq -lpq++ -L$(PGLIB)
check_answers.o: check_answers.cpp applicat.h
g++ $(CFLAGS) -c check_answers.cpp -I$(PGINCLUDE)
check_answers.cgi: check_answers.o applicat.o util.o questionnaire.o
g++ $(CFLAGS) -o check_answers.cgi check_answers.o applicat.o questionnaire.o util.o -lpq -lpq++ -L$(PGLIB)
report.o: report.cpp applicat.h
g++ $(CFLAGS) -c report.cpp -I$(PGINCLUDE)
report.cgi: report.o applicat.o util.o questionnaire.o
g++ $(CFLAGS) -o report.cgi report.o applicat.o questionnaire.o util.o -lpq -lpq++ -L$(PGLIB)
html_parser.o: html_parser.cpp applicat.h
g++ $(CFLAGS) -c html_parser.cpp
score_corrector.o: score_corrector.cpp applicat.h
g++ $(CFLAGS) -c score_corrector.cpp -I$(PGINCLUDE)
questionnaire.o: questionnaire.cpp questionnaire.h
g++ $(CFLAGS) -c questionnaire.cpp
html_parser: html_parser.o applicat.o questionnaire.o util.o
g++ $(CFLAGS) -o html_parser html_parser.o applicat.o questionnaire.o util.o
score_corrector: score_corrector.o applicat.o questionnaire.o util.o
g++ $(CFLAGS) -o score_corrector score_corrector.o applicat.o questionnaire.o util.o -lpq -lpq++ -L$(PGLIB)

View File

@ -0,0 +1,393 @@
// Classe per la gestione/parsing questionari
#include "questionnaire.h"
// In alcuni casi la parentesi finale non viene messa perchè possono esistere forme del tipo:
// <FORM method= ..> oppure <p align ...> oppure <input type=...>
#define INPUT_TAG "<INPUT"
const char* tag_list[] = { "<TITLE>",
"</TITLE>",
"<FORM",
"</FORM",
"<P",
"</P>",
"<INPUT",
"<TABLE",
"</TABLE>",
"<DIV",
"</DIV>",
NULL };
// Implementazione metodi della classe
void Questionnaire::reset()
{
int i,j; // Ma porca Eva, si può sapere perchè le specifiche ANSI vietano di definire variabili nel' inizializzazione di un for!?!?!?
_title = "";
_loaded = _parsed = _computed = FALSE;
_score = ZERO;
_max_score = 0;
for (i = 0; i < MAXQUESTIONS; i++)
{
Question_struct& qq = _questions[i];
qq.text = ""; // Azzera il testo della domanda
for (j = 0 ; j < MAXANSWERS; j++)
{
Answer_struct& aa = qq.answers[j];
aa.text = ""; // Azzera il testo della risposta
aa.user_answer = 'F';
aa.corrector_answer = 'F';
aa.p = ZERO; // Azzera il peso di questo item
}
}
}
// Abbozzo di semplice parser html ad uso e consumo AGA, per modificare
// gli html dei questionari "al volo"
void Questionnaire::parse_html(const char* name)
{
char c;
bool in_form, in_paragraph, in_table;
bracket_state bstate = none;
String current_string_tag, paragraph;
valid_tags current_tag = no_tag;
short progressivo_domande, progressivo_risposte;
progressivo_domande = progressivo_risposte = 0;
//reset();
ifstream html(name);
if (html.bad()) {
cout << "<p>Errore: impossibile leggere il file: " << name << "</p>" << endl;
return;
}
in_table = in_form = in_paragraph = FALSE;
// 2 parse or not 2 parse... This is the question...
// Note: this is a VERY VERY simple parser, designed only to find questions
// and answers in html files like test_a.htm.
// Si suppone che il file html sia sintatticamente corretto.
while (!html.eof())
{
html.get(c);
switch (c)
{
case '<': bstate = open_bracket; break;
case '>': if (bstate == open_bracket) bstate = close_bracket; break;
default: break;
}
if (bstate != none) // Compose current tag
current_string_tag += c;
if (bstate == open_bracket)
continue;
if (bstate == close_bracket)
{
// Traduce il tag da lettera a numero.
// Se non lo trova, tiene l'ultimo valido.
current_string_tag.upcase();
short i;
for ( i = 0; tag_list[i] != NULL; i++)
if (current_string_tag.index(tag_list[i]) >= 0) // Tag was recognized.
break;
i++; // Il primo elemento è no_tag
// La cosa più giusta sarebbe avere una pila di tag riconosciuti e farne il pop
if ((valid_tags) i != ignored_tag)
current_tag = (valid_tags) i;
if (current_tag == input_tag && in_paragraph)
{
paragraph += current_string_tag;
current_tag = paragraph_tag_begin;
}
current_string_tag = "";
bstate = none; // Reset bracket state
continue;
}
// Che razza di tag sei?
switch (current_tag)
{
case title_tag_begin: // Match <TITLE>
_title += c;
break;
case title_tag_end: // Match </TITLE>
current_tag = no_tag;
break;
case form_tag_begin: // Match <FORM
current_tag = no_tag;
in_form = TRUE;
in_paragraph = FALSE;
break;
case form_tag_end: // Match </FORM>
current_tag = no_tag;
in_form = FALSE;
in_paragraph = FALSE;
break;
case paragraph_tag_begin: // Match <P
if (in_form && !in_table) // Ignora i paragrafi che non sono compresi tra <FORM> e </FORM>
{
paragraph += c; // Compone tutto il paragrafo tra <P> e </P>
in_paragraph = TRUE;
}
break;
case paragraph_tag_end: // Match </P>
if (in_form && !in_table)
{
current_tag = no_tag;
in_paragraph = FALSE;
// Controlla se il paragrafo contiene eventuali <INPUT
// Per estrarre il testo deve prima togliere tutto ciò che fa parte di <INPUT ....>
// se invece non contiene nessun <INPUT allora si tratta del testo della domanda
// e lo memorizza così com'è
const int start_pos = paragraph.index(INPUT_TAG);
if (start_pos >= 0) // <INPUT... >
{
const int end_pos = paragraph.index('>', start_pos); //
paragraph.del(start_pos, end_pos - start_pos + 1);
_questions[progressivo_domande].answers[progressivo_risposte++].text = paragraph;
// Ogni MAXANSWERS cambia la domanda
// Questo tipo di logica è molto debole perchè si basa su
// di un numero fisso di possibili risposte (MAXANSWERS)
// Quindi è necessario che esse siano sempre in tal numero (MAXANSWERS appunto)
if (progressivo_risposte == MAXANSWERS)
{
progressivo_risposte = 0;
progressivo_domande++;
if (progressivo_domande == MAXQUESTIONS) // Non eccedere MAXQUESTIONS
progressivo_domande--;
}
}
else
_questions[progressivo_domande].text = paragraph;
paragraph = "";
}
break;
case table_tag_begin:
in_table = TRUE;
break;
case table_tag_end:
in_table = FALSE;
break;
default:
break; // Ignore any other tag...(ignored_tag or no_tag)
}
}
_parsed = TRUE;
}
// S1 contiene il correttore, S2 contiene le risposte fornite
bool Questionnaire::load(const String& s1, const String& s2)
{
reset();
_loaded = !s1.empty() && !s2.empty();
if (!_loaded) {
cout << "<p>Errore: impossibile caricare il correttore o le risposte fornite</p>" << endl;
return FALSE;
}
// Il formato delle stringhe da passare in S1 ed S2 (correttore e risposte) è siffatto:
// Q_<numero domanda>=<V|F><V|F><V|F><V|F>;Q_<numero domanda>=<V|F><V|F><V|F><V|F>; ...etc
// I valori delle risposte possono essre V (vero) oppure F (falso); se indicato un blank
// lo si tratta come risposta non presente, ovvero per identificare la fine delle risposte
// possibili per ogni domanda;
// serve per riconoscere quando fermare il test per ogni risposta data in calc_score().
// Esempio: Q_1=VVFV;Q_2=VFFV;Q_3=FFVF
// Attenzione, le risposte possibili sono sempre 5, ma nel correttore ne vengono indicate solo 4;
// questo perche' la quinta viene presa in considerazione solo nel caso le precedenti
// siano tutte V o tutte F. La quinta domanda corrisponde a "nessuna delle precedenti"
// In questa funzione si carica la matrice correttore con i punteggi possibili per le
// varie risposte fornite; ecco gli esempi possibili con i vari punteggi:
// VVVV ---> +1/4, +1/4, +1/4, +1/4, -1
// FFFF ---> -1/4, -1/4, -1/4, -1/4, +1
// VFFF ---> +1, -1/3, -1/3, -1/3, 0
// VVFF ---> +1/2, +1/2, -1/2, -1/2, 0
// VVVF ---> +1/3, +1/3, +1/3, -1, 0
// Ecc. ecc. per tutte le varie combinazioni possibili. In pratica il concetto è: 1 punto
// totale per ogni domanda, tale punto va suddiviso a seconda delle risposte corrette. Nel
// caso di risposte tutte sbagliate o tutte giuste entra in gioco anche la domanda nr. 5
// Quindi per un questionario con 20 domande avremo un massimo di 20 punti ed un minimo
// assoluto di -20.
String separator, risposte;
String rs[MAXQUESTIONS], qs[2], ns[2];
int numr, numi;
for (int x = 0; x < 2 ; x++) {
const bool parse_corrector = x == 0;
risposte = parse_corrector ? s1 : s2;
trim(risposte);
separator = ";";
numr = split(risposte, rs, MAXQUESTIONS, separator);
for (int i=0; i < numr; i++) {
separator = "=";
numi = split(rs[i], qs, 2, separator);
// qs[0] contiene Q_<numero domanda>
// qs[1] contiene VVVV FFFF VFFV etc
if (numi == 2) {
if (parse_corrector)
_max_score++;
separator = "_";
split(qs[0],ns,2,separator);
const int qn = atoi(ns[1]) -1;
String& qs1 = qs[1];
qs1.upcase();
const int r_vere = qs1.freq('V'); // Numero di risposte vere
const int r_false = MAXANSWERS - r_vere - 1; // Numero di risposte false
Rational p_v(1, r_vere != 0 ? r_vere : 1); // 1/4, 1/3, 1/2, 1
Rational p_f(1, r_false != 0 ? r_false : 1); // -1/4, -1/3, -1/2. -1
p_f.negate(); // Cambia segno per i punti in negativo
const int l = qs1.length();
if (parse_corrector && r_vere == 0)
_questions[qn].answers[MAXANSWERS-1].corrector_answer = 'V';
for (int j = 0; j < MAXANSWERS; j++) {
Answer_struct& ans = _questions[qn].answers[j];
if (parse_corrector) {
if (j<l) {
ans.p = qs1[j] == 'V' ? p_v : p_f;
ans.corrector_answer = qs1[j]; // Memorizza le risposte del correttore ed il "peso"
}
else
if (r_vere == 0)
ans.p = p_v;
else
if (r_false == 0)
ans.p = p_f;
}
else
if (j<l)
ans.user_answer = qs1[j]; // Memorizza le risposte dell'utente
}
}
_loaded = TRUE;
}
}
// Debug Stuff
#ifdef DBG
cout << "<H2>Correttore</H2><BR><BR>" << endl;
for (int j = 0; j < MAXQUESTIONS; j++) {
cout << "Domanda " << j+1 << ":<BR>";
for (int k = 0; k < MAXANSWERS; k++) {
cout << _questions[j].answers[k].corrector_answer << "__";
//Rational& rr = _questions[j].answers[k].p;
//cout << rr.numerator() << "/" << rr.denominator() << ";";
}
cout << "<BR>" << endl;
}
_loaded = FALSE;
#endif
return _loaded;
}
void Questionnaire::dump_html(const String& corso, const int modulo, const char testnum)
{
if (!_parsed) {
String nome;
nome = FAD_ROOT;
nome += corso;
nome += "/documenti/M";
nome += itoa(modulo);
nome += "/test_";
nome += testnum;
nome += ".htm";
parse_html(nome);
}
if (_parsed && _loaded)
{
int i,j;
Rational punteggio;
punteggio = calc_score();
cout << "<CENTER>" << endl;
cout << "<H2>" << _title << "</H2>" << endl;
cout << "</CENTER>" << endl;
cout << "<P>Punteggio ottenuto nel test: <STRONG>" << dtoa((double)punteggio) ;
cout << " su di un massimo di " << itoa(_max_score) << "</STRONG><BR><BR><BR>" << endl;
cout << "<P>Le caselle di sinistra identificano le risposte corrette, quelle di destra le risposte fornite</P>" << endl;
cout << "<P>Ogni risposta che differisce dal correttore viene videnziata in rosso</p>" <<endl;
cout << "<BR><BR>" << endl;
cout << "<FORM>" << endl; // Senza il tag <FORM> non visualizza i checkbox!
for (i=0;i<MAXQUESTIONS;i++)
if (!_questions[i].text.empty())
{
cout << "<P><STRONG>" << _questions[i].text << "</STRONG></P>" << endl; // Test della domanda
for (j=0;j<MAXANSWERS;j++)
{
Answer_struct& ans = _questions[i].answers[j];
const bool is_right = ans.user_answer == ans.corrector_answer;
cout << "<P><INPUT TYPE=\"CHECKBOX\" NAME=\"C_" << i+1 << "_" << j+1;
cout << "\" VALUE=\"ON\" ";
if (ans.corrector_answer == 'V')
cout << "CHECKED ";
cout << "onClick=\"return false;\">"; // Risposte del correttore
cout << "<INPUT TYPE=\"CHECKBOX\" NAME=\"U_" << i+1 << "_" << j+1;
cout << "\" VALUE=\"ON\" ";
if (ans.user_answer == 'V')
cout << "CHECKED ";
cout << "onClick=\"return false;\">"; // Risposte dell'utente
if (!is_right) // Se la risposta fornita non corrisponde a quella del correttore, evidenzia in rosso la risposta data.
cout << "<font color=\"#FF0000\">";
cout << _questions[i].answers[j].text;
if (!is_right)
cout << "</font>";
cout << "</P>" << endl;
}
}
cout << "</FORM>" << endl;
}
else
cout << "<p>Errore: per eseguire il dump dei tests è necessario caricare il correttore, le risposte e specificare il corso, il modulo ed il numero di test.</p>" << endl;
}
Rational Questionnaire::calc_score()
{
if (_loaded && !_computed)
{
// Calcolo punteggio: per ogni risposta corrispondente a quella del correttore
// si ottiene un punto.
_computed = TRUE;
for (int i=0; i < MAXQUESTIONS; i++)
{
for (int j=0; j < MAXANSWERS; j++) {
Answer_struct& ans = _questions[i].answers[j];
if (((Rational&)ans.p).numerator() != 0)
if (ans.user_answer == 'V') // Per ogni risposta vera fornita, aggiunge il punteggio fornito dal correttore
_score += ((Rational&) ans.p);
}
}
}
return _score;
}

View File

@ -0,0 +1,79 @@
#include "defines.h"
struct Answer_struct
{
String text; // Testo dell'item di risposta
char user_answer; // Risposta data dall'utente (V o F)
char corrector_answer; // Risposta memorizzata nel correttore
Rational p; // "Peso" della risposta giusta/sbagliata: 1, 1/4, 1/2, 1/3, -1/3 -1/2 ecc
};
struct Question_struct
{
String text; // Testo della domanda
Answer_struct answers[MAXANSWERS];
};
/*
Modo di utilizzo:
Questionnaire q;
s1 = "Q_1=VFFF;Q_2=VVVV;Q_3=FVFV;..."
s2 = "Q_1=FFFVF;Q_2=FVFVF;Q_3=VVFFF;..."
q.load(s1,s2);
Il metodo load() azzera il questionario ed è praticamente il primo metodo da
invocare; dove s1 è il correttore ed s2 sono le risposte fornite dall'utente.
Questo metodo carica il correttore e le risposte fornite, predisponendo il calcolo
del punteggio.
Dopo aver chiamato load() è si possono chiamare i metodi dump_html() o calc_score()
indifferentemente l'uno dall'altro.
Rational r = q.calc_score();
il metodo calc_score() ritorna il punteggio calcolato nel test in base al correttore ed alle
risposte fornite in load()
q.dump_html("volare", 10, 'a');
il metodo dump_html() scrive sullo stdout l'html del test eseguito, contrassegnando
con le "X" i checkbox del correttore e delle risposte fornite dall'utente, evidenziando in
rosso le risposte errate rispetto al correttore. Si ricorda che tale html non è una
pagina completa, per permettere di fare aggiunte prima o dopo di esso.
In conclusione prima di usare i 2 metodi dump_html() o calc_score() è necessario
aver fatto comunque un load() del correttore.
*/
class Questionnaire
{
private:
enum bracket_state { none, open_bracket, close_bracket };
enum valid_tags { no_tag, title_tag_begin, title_tag_end, form_tag_begin, form_tag_end,
paragraph_tag_begin, paragraph_tag_end, input_tag, table_tag_begin,
table_tag_end, div_tag_begin, div_tag_end, ignored_tag };
String _title;
Question_struct _questions[MAXQUESTIONS];
bool _loaded, _parsed, _computed;
Rational _score;
int _max_score;
protected:
void parse_html(const char* name);
public:
void reset();
Rational calc_score();
bool load(const String& s1, const String& s2);
void dump_html(const String& corso, const int modulo, const char testnum);
Questionnaire() { reset();}
virtual ~Questionnaire() {};
};

219
webcgi/formazione/report.cpp Executable file
View File

@ -0,0 +1,219 @@
// report.cgi: applicazione per avere qualche piccolo report/status
// sui partecipanti ai corsi
#include <libpq++.h>
#include "applicat.h"
#include "questionnaire.h"
class Report_Application : public Application
{
private:
String _dbname;
String _utente;
String _azione; //ACCESSI, VERIFICHE, LOGGATI
String _modulo; // Numero del modulo
String _testnum; // Livello di test
String _correct_answers;
String _corso;
Questionnaire _questionario;
PgEnv _environment;
PgDatabase *_db;
protected:
virtual bool create();
virtual bool destroy();
virtual void main_func();
void report();
public:
Report_Application() {_db = NULL;}
virtual ~Report_Application() {};
};
bool Report_Application::create()
{
_modulo = "";
_testnum = "";
_corso = "";
_dbname = POSTGRES_DB;
_environment.Port(POSTGRES_PORT);
_environment.Host(POSTGRES_HOST);
print_header("Report per utenti");
char *t1, *t2;
char *ccc = getenv("CONTENT_LENGTH");
int cl = ccc != NULL ? atoi(ccc) : 0;
if (cl < 512)
for (int x = 0; cl && (!feof(stdin)); x++) {
t1 = fmakeword(stdin, '&', &cl);
t2 = makeword(t1, '=');
unescape_url(t1);
unescape_url(t2);
if (!strcmp(t2,"AZIONE"))
_azione = t1;
if (!strcmp(t2,"UTENTE"))
_utente = t1;
if (!strcmp(t2,"MODULO"))
_modulo = t1;
if (!strcmp(t2,"TESTNUM"))
_testnum = t1;
}
else {
cout << "<H2>Troppi dati inviati</H2><BR><BR>" << endl;
cout << "<P>L'applicazione ha ricevuto troppi dati sul buffer d'ingresso.</P><BR>" << endl;
return FALSE;
}
return TRUE;
}
bool Report_Application::destroy()
{
print_footer();
return TRUE;
}
void Report_Application::report()
{
// The Last One... Query di selezione per ogni azione diversa
String command, command1;
if (_azione == "LOGGATI") {
command = "SELECT * FROM UTENTI WHERE logged='t'";
cout << "<H2>Elenco utenti collegati</H2><BR><BR>" << endl;
}
else
if (_azione == "ACCESSI") {
command = "SELECT * FROM ACCESSI WHERE loginname='";
command += _utente;
command += "'";
cout << "<H2>Elenco accessi effettuati per l'utente " << _utente << "</H2><BR><BR>" << endl;
}
else
if (_azione == "VERIFICHE") {
command = "SELECT * FROM VERIFICHE WHERE loginname='";
command += _utente;
command += "'";
if (!_modulo.empty() && !_testnum.empty()){
command += " AND MODULO=";
command += _modulo;
command += " AND TESTNUM='";
command += _testnum;
command += "'";
// Selezione per reperire il correttore
command1 = "SELECT * FROM CORRETTORI WHERE MODULO=";
command1 += _modulo;
command1 += " AND TESTNUM='";
command1 += _testnum;
command1 += "'";
_db->ExecCommandOk(command1);
if (_db->Tuples() > 0)
_correct_answers = _db->GetValue(0,"risposte");
// Selezione per reperire a quale corso appartiene l'utente
command1 = "SELECT * FROM UTENTI WHERE loginname='";
command1 += _utente;
command1 += "'";
_db->ExecCommandOk(command1);
if (_db->Tuples() > 0)
_corso = _db->GetValue(0,"course");
}
cout << "<H2>Elenco verifiche effettuate per l'utente " << _utente << "</H2><BR><BR>" << endl;
}
else
command = "SELECT * FROM UTENTI WHERE loginname='_'"; // Comando per ritornare 0 Tuple
_db->ExecCommandOk(command);
const int tuples = _db->Tuples();
if (tuples > 0) {
// Intestazione tabella
cout << "<CENTER><TABLE BORDER>" << endl;
cout << "<TR>";
if (_azione == "LOGGATI")
cout << "<TH>Log-name</TH><TH>Accesso in data</TH></TR>" << endl;
else
if (_azione == "ACCESSI")
cout << "<TH>Log-Name</TH><TH>Ingresso</TH><TH>Uscita</TH></TR>" << endl;
else
if (_azione == "VERIFICHE")
cout << "<TH>Log-name</TH><TH>Modulo</TH><TH>Liv. Test</TH><TH>Data verifica</TH><TH>Punteggio</TH></TR>" << endl;
for (int i = 0; i < tuples; i++) { // Ciclo sulle tuple ritornate
cout << "<TR>";
if (_azione == "LOGGATI")
cout << "<TD>" << _db->GetValue(i,"loginname") << "</TD><TD>" << _db->GetValue(i,"logindate") << "</TD>" << endl;
else
if (_azione == "ACCESSI")
cout << "<TD>" << _db->GetValue(i,"loginname") << "</TD><TD>" << _db->GetValue(i,"logindate") << "</TD><TD>" << _db->GetValue(i,"logoutdate") << "</TD>" << endl;
else
if (_azione == "VERIFICHE")
cout << "<TD>" << _db->GetValue(i,"loginname")
<< "</TD><TD>" << _db->GetValue(i,"modulo") << "</TD><TD>"
<< _db->GetValue(i,"testnum") << "</TD><TD>"
<< _db->GetValue(i,"data") << "</TD><TD>"
<< _db->GetValue(i,"punteggio") << "</TD>" << endl;
cout << "</TR>" << endl;
}
cout << "</TABLE></CENTER>" << endl;
if (_azione == "VERIFICHE" && !_modulo.empty() && !_testnum.empty()) {
String user_answers, corso;
trim(_corso);
for (int i=0; i<tuples; i++) {
user_answers = _db->GetValue(i,"risposte");
_questionario.load(_correct_answers,user_answers);
_questionario.dump_html(_corso, atoi(_modulo), _testnum[0]);
}
}
}
else
cout << "<p>Nessun record presente per la query selezionata</p>" << endl;
return;
}
void Report_Application::main_func()
{
if (_azione.empty() || (_azione != "LOGGATI" && _utente.empty())) {
cout << "<H2>Impossibile eseguire il report</H2><BR><BR>" << endl;
cout << "<P>E' necessario specificare il tipo di report e/o l'utente.</P><BR>";
return;
}
_db = new PgDatabase(_environment, _dbname);
if ( _db->ConnectionBad() )
print_database_error();
else
report();
delete _db;
return;
}
int main(int argc, char* argv[])
{
Report_Application* a = new Report_Application();
a->run(argc, argv);
delete a;
exit(0);
}

3363
webcgi/formazione/rfc1945.txt Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,151 @@
// Applicazione per la correzione dei punteggi
#include <libpq++.h>
#include "applicat.h"
#include "questionnaire.h"
class Score_Application : public Application
{
private:
PgDatabase *_db1, *_db2, *_db3;
Questionnaire _questionario;
PgEnv _environment;
String _dbname;
protected:
virtual bool create();
virtual bool destroy();
virtual void main_func();
void correct();
public:
Score_Application() {}
virtual ~Score_Application() {};
};
bool Score_Application::create()
{
_dbname = POSTGRES_DB;
_environment.Port(POSTGRES_PORT);
_environment.Host(POSTGRES_HOST);
return TRUE;
}
bool Score_Application::destroy()
{
return TRUE;
}
void Score_Application::correct()
{
String command, utente, modulo, testnum, user_answers, corrector_answers;
double punteggio;
int num=0, nv = 0;
//command = "SELECT * FROM VERIFICHE";
command = "SELECT * FROM CORRETTORI";
/*command += modulo;
command += " AND testnum='";
command += testnum;
command += "'";*/
_db1->ExecCommandOk(command);
const int tuples = _db1->Tuples();
if (tuples > 0) {
for (int i = 0; i<tuples; i++) // Ciclo su tutti i correttori
{
modulo = _db1->GetValue(i,"modulo");
testnum = _db1->GetValue(i,"testnum");
corrector_answers = _db1->GetValue(i,"risposte");
command = "SELECT * FROM VERIFICHE WHERE modulo=";
command += modulo;
command += " AND TESTNUM='";
command += testnum;
command += "'";
_db2->ExecCommandOk(command);
const int tups = _db2->Tuples();
if (tups > 0) {
for (int j=0;j<tups;j++) {
double score;
nv++;
utente = _db2->GetValue(j,"loginname");
punteggio = strtod(_db2->GetValue(j,"punteggio"),NULL);
user_answers = _db2->GetValue(j,"risposte");
_questionario.load(corrector_answers, user_answers);
score = strtod(dtoa(_questionario.calc_score()),NULL);
if (score != punteggio) {
num++;
cout << "Punteggio non congruente:" << endl << " Utente: " << utente << endl;
cout << " Punteggio calcolato= " << dtoa(score) << endl;
cout << " Punteggio mem.= " << dtoa(punteggio) << endl;
cout << " Modulo: " << modulo << " Livello : " << testnum << endl;
command = "UPDATE VERIFICHE SET punteggio=";
command += dtoa(_questionario.calc_score());
command += " WHERE loginname='";
command += utente;
command += "' AND MODULO=";
command += modulo;
command += " AND TESTNUM='";
command += testnum;
command += "'";
_db3->ExecCommandOk(command);
}
}
}
}
cout << endl << "Numero totale verifiche : " << nv << " di cui errate: " << num << endl;
}
else
cout << "Nessuna verifica presente nel database" << endl;
}
void Score_Application::main_func()
{
_db1 = new PgDatabase(_environment, _dbname);
_db2 = new PgDatabase(_environment, _dbname);
_db3 = new PgDatabase(_environment, _dbname);
if ( _db1->ConnectionBad() || _db2->ConnectionBad()|| _db3->ConnectionBad())
cout << "Errore in connessione al database" << endl;
else
correct();
delete _db1; // Termina la transazione
delete _db2;
delete _db3;
}
int main(int argc, char* argv[])
{
Score_Application* a = new Score_Application();
a->run(argc, argv);
delete a;
exit(0);
}

12
webcgi/formazione/tracciato Executable file
View File

@ -0,0 +1,12 @@
\connect - angelo
CREATE TABLE accessi (progressivo int4, loginname char(30), logindate timestamp, logoutdate timestamp);
\connect - angelo
CREATE TABLE correttori (progressivo int4, modulo int4, testnum char, risposte char(254));
\connect - angelo
CREATE TABLE moduli (modulenum int4, modulename char(30), descrizione char(50), lasttest char, faked bool);
\connect - angelo
CREATE TABLE progressivi (progaccessi int4, progverifiche int4);
\connect - angelo
CREATE TABLE utenti (loginname char(30), realname char(50), email char(30), modules char(64), logindate timestamp, logged bool, progaccesso int4, verifiche char(64), telefono char(50), course char(20));
\connect - angelo
CREATE TABLE verifiche (progressivo int4, loginname char(30), data timestamp, modulo int4, testnum char, risposte char(254), punteggio float8);

166
webcgi/formazione/util.cpp Executable file
View File

@ -0,0 +1,166 @@
#include "defines.h"
#define LF 10
#define CR 13
static char __stringa[64];
char* itoa(int i)
{
sprintf(__stringa, "%d", i);
return __stringa;
}
char* ltoa(long l)
{
sprintf(__stringa, "%ld", l);
return __stringa;
}
char* dtoa(double d)
{
sprintf(__stringa, "%6.4f", d);
return __stringa;
}
void getword(char *word, char *line, char stop) {
int x = 0,y;
for(x=0;((line[x]) && (line[x] != stop));x++)
word[x] = line[x];
word[x] = '\0';
if(line[x]) ++x;
y=0;
while((line[y++] = line[x++]));
}
char *makeword(char *line, char stop) {
int x = 0,y;
char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));
for(x=0;((line[x]) && (line[x] != stop));x++)
word[x] = line[x];
word[x] = '\0';
if(line[x]) ++x;
y=0;
while((line[y++] = line[x++]));
return word;
}
char *fmakeword(FILE *f, char stop, int *cl) {
int wsize;
char *word;
int ll;
wsize = 102400;
ll=0;
word = (char *) malloc(sizeof(char) * (wsize + 1));
while(1) {
word[ll] = (char)fgetc(f);
if(ll==wsize) {
word[ll+1] = '\0';
wsize+=102400;
word = (char *)realloc(word,sizeof(char)*(wsize+1));
}
--(*cl);
if((word[ll] == stop) || (feof(f)) || (!(*cl))) {
if(word[ll] != stop) ll++;
word[ll] = '\0';
return word;
}
++ll;
}
}
char x2c(char *what) {
register char digit;
digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
digit *= 16;
digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
return(digit);
}
void unescape_url(char *url) {
register int x,y;
for(x=0,y=0;url[y];++x,++y) {
if((url[x] = url[y]) == '%') {
url[x] = x2c(&url[y+1]);
y+=2;
}
}
url[x] = '\0';
}
void plustospace(char *str) {
register int x;
for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';
}
int rind(char *s, char c) {
register int x;
for(x=strlen(s) - 1;x != -1; x--)
if(s[x] == c) return x;
return -1;
}
int getline(char *s, int n, FILE *f) {
register int i=0;
while(1) {
s[i] = (char)fgetc(f);
if(s[i] == CR)
s[i] = fgetc(f);
if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) {
s[i] = '\0';
return (feof(f) ? 1 : 0);
}
++i;
}
}
void send_fd(FILE *f, FILE *fd)
{
char c;
while (1) {
c = fgetc(f);
if(feof(f))
return;
fputc(c,fd);
}
}
off_t fsize(const char* n)
{
struct stat statbuf;
stat(n, &statbuf);
return statbuf.st_size;
}
void trim(String& s)
{
const int l = s.length();
int i,f,n;
f = 0;
n = 0;
for (i = l-1; i>=0; i--)
if (s[i] != ' ')
break;
if (i >= 0 && i < l-1)
{
f = i+1;
n = l-i-1;
}
s.del(f, n);
}