1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294 |
- /* EX_VOTE.C - This program demonstrates an online voting program that is */
- /* written using OpenDoors. The Vote program allows users to */
- /* create questions or surveys for other users to respond to. */
- /* Users are also able to view the results of voting on each */
- /* topic. The program supports up to 200 questions, and can be */
- /* configured to either allow or disallow viewing of results */
- /* prior to voting. */
- /* */
- /* This program shows how to do the following: */
- /* */
- /* - How to display text using od_printf(), using imbedded */
- /* strings to change the display color. */
- /* - Add support for standard command-line options. */
- /* - Use the OpenDoors configuration file system, including */
- /* adding your own configuration file options. */
- /* - Activate the OpenDoors log file system and write */
- /* information to the log file. */
- /* - Display a menu from an external ASCI/ANSI/Avatar/RIP */
- /* file. */
- /* - How to setup a user file and general data file, and how */
- /* to access it in a multi-node compatible way (if */
- /* MULTINODE_AWARE is defined). */
- /* */
- /* To recompile this program, follow the instructions in the */
- /* OpenDoors manual. For a DOS compiler, be sure to set your */
- /* compiler to use the large memory model, and add the */
- /* ODOORL.LIB file to your project/makefile. */
- /* Uncomment the following line for multi-node compatible file access. */
- /* #define MULTINODE_AWARE */
- /* Include standard C header files required by Vote. */
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- #include <errno.h>
- #include <ctype.h>
- /* Include the OpenDoors header file. This line must be done in any program */
- /* using OpenDoors. */
- #include "OpenDoor.h"
- #ifdef MULTINODE_AWARE
- #include <filewrap.h>
- #endif
- #include "genwrap.h"
- /* Manifest constants used by Vote */
- #define NO_QUESTION -1
- #define NEW_ANSWER -1
- #define QUESTIONS_VOTED_ON 0x0001
- #define QUESTIONS_NOT_VOTED_ON 0x0002
- #define MAX_QUESTIONS 200
- #define MAX_USERS 30000
- #define MAX_ANSWERS 15
- #define QUESTION_STR_SIZE 71
- #define ANSWER_STR_SIZE 31
- #define USER_FILENAME "vote.usr"
- #define QUESTION_FILENAME "vote.qst"
- #define FILE_ACCESS_MAX_WAIT 20
- #define QUESTION_PAGE_SIZE 17
- /* Structure of records stored in the VOTE.USR file */
- typedef struct
- {
- char szUserName[36];
- BYTE bVotedOnQuestion[MAX_QUESTIONS];
- } tUserRecord;
-
- tUserRecord CurrentUserRecord;
- int nCurrentUserNumber;
- /* Structure of records stored in the VOTE.QST file */
- typedef struct
- {
- char szQuestion[72];
- char aszAnswer[MAX_ANSWERS][32];
- INT32 nTotalAnswers;
- DWORD auVotesForAnswer[MAX_ANSWERS];
- DWORD uTotalVotes;
- DWORD bCanAddAnswers;
- char szCreatorName[36];
- time_t lCreationTime;
- } tQuestionRecord;
- /* Global variables. */
- int nViewResultsFrom = QUESTIONS_VOTED_ON;
- int nQuestionsVotedOn = 0;
- /* Prototypes for functions that form EX_VOTE */
- void CustomConfigFunction(char *pszKeyword, char *pszOptions);
- void BeforeExitFunction(void);
- void VoteOnQuestion(void);
- void ViewResults(void);
- int GetQuestion(int nQuestion, tQuestionRecord *pQuestionRecord);
- void AddQuestion(void);
- int ChooseQuestion(int nFromWhichQuestions, char *pszTitle, int *nLocation);
- void DisplayQuestionResult(tQuestionRecord *pQuestionRecord);
- int ReadOrAddCurrentUser(void);
- void WriteCurrentUser(void);
- FILE *ExclusiveFileOpen(char *pszFileName, char *pszMode, int *phHandle);
- void ExclusiveFileClose(FILE *pfFile, int hHandle);
- void WaitForEnter(void);
- /* main() or WinMain() function - Program execution begins here. */
- #ifdef ODPLAT_WIN32
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
- LPSTR lpszCmdLine, int nCmdShow)
- #else
- int main(int argc, char *argv[])
- #endif
- {
- /* Variable to store user's choice from the menu */
- char chMenuChoice = '\0';
- char chYesOrNo;
- #ifdef ODPLAT_WIN32
- /* In Windows, pass in nCmdShow value to OpenDoors. */
- od_control.od_cmd_show = nCmdShow;
- /* Ignore unused parameters. */
- (void)hInstance;
- (void)hPrevInstance;
- #endif
-
- /* Set program's name for use by OpenDoors. */
- strcpy(od_control.od_prog_name, "Vote");
- strcpy(od_control.od_prog_version, "Version 6.00");
- strcpy(od_control.od_prog_copyright, "Copyright 1991-1996 by Brian Pirie");
- /* Call the standard command-line parsing function. You will probably */
- /* want to do this in most programs that you write using OpenDoors, as it */
- /* automatically provides support for many standard command-line options */
- /* that will make the use and setup of your program easer. For details, */
- /* run the vote program with the /help command line option. */
- #ifdef ODPLAT_WIN32
- od_parse_cmd_line(lpszCmdLine);
- #else
- od_parse_cmd_line(argc, argv);
- #endif
- /* Enable use of OpenDoors configuration file system. */
- od_control.od_config_file = INCLUDE_CONFIG_FILE;
- /* Set function to process custom configuration file lines. */
- od_control.od_config_function = CustomConfigFunction;
- /* Include the OpenDoors multiple personality system, which allows */
- /* the system operator to set the sysop statusline / function key set */
- /* to mimic the BBS software of their choice. */
- od_control.od_mps = INCLUDE_MPS;
-
- /* Include the OpenDoors log file system, which will record when the */
- /* door runs, and major activites that the user performs. */
- od_control.od_logfile = INCLUDE_LOGFILE;
- /* Set filename for log file. If not set, DOOR.LOG will be used by */
- /* default. */
- strcpy(od_control.od_logfile_name, "vote.log");
- /* Set function to be called before program exits. */
- od_control.od_before_exit = BeforeExitFunction;
- /* Initialize OpenDoors. This function call is optional, and can be used */
- /* to force OpenDoors to read the door informtion file and begin door */
- /* operations. If a call to od_init() is not included in your program, */
- /* OpenDoors initialization will be performed at the time of your first */
- /* call to any OpenDoors function. */
- od_init();
- /* Call the Vote function ReadOrAddCurrentUser() to read the current */
- /* user's record from the Vote user file, or to add the user to the */
- /* file if this is the first time that they have used Vote. */
- if(!ReadOrAddCurrentUser())
- {
- /* If unable to obtain a user record for the current user, then exit */
- /* the door after displaying an error message. */
- od_printf("Unable to access user file. File may be locked or full.\n\r");
- WaitForEnter();
- od_exit(1, FALSE);
- }
- /* Loop until the user choses to exit the door. For each iteration of */
- /* this loop, we display the main menu, get the user's choice from the */
- /* menu, and perform the appropriate action for their choice. */
- while(chMenuChoice != 'E' && chMenuChoice != 'H')
- {
- /* Clear the screen */
- od_clr_scr();
- /* Display main menu. */
-
- /* First, attempt to display menu from an VOTE.ASC/ANS/AVT/RIP file. */
- if((chMenuChoice = od_hotkey_menu("VOTE", "VRADPEH", TRUE)) == 0)
- {
- /* If the VOTE file could not be displayed, display our own menu. */
- od_printf("`bright red` Vote - OpenDoors 6.00 example program\n\r");\
- od_printf("`dark red`");
- if(od_control.user_ansi || od_control.user_avatar)
- {
- od_repeat((unsigned char)196, 79);
- }
- else
- {
- od_repeat('-', 79);
- }
- od_printf("\n\r\n\r\n\r`dark green`");
- od_printf(" [`bright green`V`dark green`] Vote on a question\n\r\n\r");
- od_printf(" [`bright green`R`dark green`] View the results of question\n\r\n\r");
- od_printf(" [`bright green`A`dark green`] Add a new question\n\r\n\r");
- od_printf(" [`bright green`P`dark green`] Page system operator for chat\n\r\n\r");
- od_printf(" [`bright green`E`dark green`] Exit door and return to the BBS\n\r\n\r");
- od_printf(" [`bright green`H`dark green`] End call (hangup)\n\r\n\r\n\r");
- od_printf("`bright white`Press the key corresponding to the option of your choice. (%d mins)\n\r`dark green`",
- od_control.user_timelimit);
- \
- /* Get the user's choice from the main menu. This choice may only be */
- /* V, R, A, D, P, E or H. */
- chMenuChoice = od_get_answer("VRADPEH");
- }
- /* Perform the appropriate action based on the user's choice */
- switch(chMenuChoice)
- {
- case 'V':
- /* Call Vote's function to vote on question */
- VoteOnQuestion();
- break;
-
- case 'R':
- /* Call Vote's function to view the results of voting */
- ViewResults();
- break;
-
- case 'A':
- /* Call Vote's function to add a new question. */
- AddQuestion();
- break;
-
- case 'P':
- /* If the user pressed P, allow them page the system operator. */
- od_page();
- break;
- case 'H':
- /* If the user pressed H, ask whether they wish to hangup. */
- od_printf("\n\rAre you sure you wish to hangup? (Y/N) ");
- /* Get user's response */
- chYesOrNo = od_get_answer("YN");
- if(chYesOrNo == 'N')
- {
- /* If user answered no, then reset menu choice, so that */
- /* program will not exit. */
- chMenuChoice = '\0';
- }
- break;
- }
- }
- if(chMenuChoice == 'H')
- {
- /* If the user chooses to hangup, then hangup when exiting. */
- od_exit(0, TRUE);
- }
- else
- {
- /* Otherwise, exit normally (without hanging up). */
- od_printf("Returning to BBS, please wait...\n\r");
- od_exit(0, FALSE);
- }
- return(0);
- }
- /* CustomConfigFunction() is called by OpenDoors to process custom */
- /* configuration file keywords that Vote uses. */
- void CustomConfigFunction(char *pszKeyword, char *pszOptions)
- {
- if(stricmp(pszKeyword, "ViewUnanswered") == 0)
- {
- /* If keyword is ViewUnanswered, set local variable based on contents */
- /* of options string. */
- if(stricmp(pszOptions, "Yes") == 0)
- {
- nViewResultsFrom = QUESTIONS_VOTED_ON | QUESTIONS_NOT_VOTED_ON;
- }
- else if(stricmp(pszOptions, "No") == 0)
- {
- nViewResultsFrom = QUESTIONS_VOTED_ON;
- }
- }
- }
- /* Vote configures OpenDoors to call the BeforeExitFunction() before */
- /* the door exists for any reason. You can use this function to close any */
- /* files or perform any other operations that you wish to have peformed */
- /* before OpenDoors exists for any reason. The od_control.od_before_exit */
- /* variable sets the function to be called before program exit. */
- void BeforeExitFunction(void)
- {
- char szLogMessage[80];
-
- /* Write number of messages voted on to log file. */
- sprintf(szLogMessage, "User has voted on %d question(s)",
- nQuestionsVotedOn);
- od_log_write(szLogMessage);
- }
- /* Vote calls the VoteOnQuestion() function when the user chooses the */
- /* vote command from the main menu. This function displays a list of */
- /* available topics, asks for the user's answer to the topic they select, */
- /* and display's the results of voting on that topic. */
- void VoteOnQuestion(void)
- {
- int nQuestion;
- int nAnswer;
- tQuestionRecord QuestionRecord;
- char szNewAnswer[ANSWER_STR_SIZE];
- char szUserInput[3];
- FILE *fpFile;
- int hFile;
- int nPageLocation = 0;
- /* Loop until the user chooses to return to the main menu, or until */
- /* there are no more questions to vote on. */
- for(;;)
- {
- /* Allow the user to choose a question from the list of questions */
- /* that they have not voted on. */
- nQuestion = ChooseQuestion(QUESTIONS_NOT_VOTED_ON,
- " Vote On A Question\n\r",
- &nPageLocation);
-
- /* If the user did not choose a question, return to main menu. */
- if(nQuestion == NO_QUESTION)
- {
- return;
- }
- /* Read the question chosen by the user. */
- if(!GetQuestion(nQuestion, &QuestionRecord))
- {
- /* If unable to access file, return to main menu. */
- return;
- }
-
- /* Don't allow addition of new answers if maximum number of answers */
- /* have already been added. */
- if(QuestionRecord.nTotalAnswers >= MAX_ANSWERS)
- {
- QuestionRecord.bCanAddAnswers = FALSE;
- }
-
- /* Loop until user makes a valid respose. */
- for(;;)
- {
- /* Display question to user. */
- /* Clear the screen. */
- od_clr_scr();
- /* Display question itself. */
- od_printf("`bright red`%s\n\r\n\r", QuestionRecord.szQuestion);
- /* Loop for each answer to the question. */
- for(nAnswer = 0; nAnswer < QuestionRecord.nTotalAnswers; ++nAnswer)
- {
- /* Display answer number and answer. */
- od_printf("`bright green`%d. `dark green`%s\n\r",
- nAnswer + 1,
- QuestionRecord.aszAnswer[nAnswer]);
- }
- /* Display prompt to user. */
- od_printf("\n\r`bright white`Enter answer number, ");
- if(QuestionRecord.bCanAddAnswers)
- {
- od_printf("[A] to add your own response, ");
- }
- od_printf("[Q] to quit: `dark green`");
-
- /* Get response from user. */
- od_input_str(szUserInput, 2, ' ', 255);
- /* Add a blank line. */
- od_printf("\n\r");
-
- /* If user entered Q, return to main menu. */
- if(stricmp(szUserInput, "Q") == 0)
- {
- return;
- }
- /* If user enetered A, and adding answers is premitted ... */
- else if (stricmp(szUserInput, "A") == 0
- && QuestionRecord.bCanAddAnswers)
- {
- /* ... Prompt for answer from user. */
- od_printf("`bright green`Please enter your new answer:\n\r");
- od_printf("`dark green`[------------------------------]\n\r ");
-
- /* Get string from user. */
- od_input_str(szNewAnswer, ANSWER_STR_SIZE - 1, ' ', 255);
-
- /* Record that user entered a new answer answer. */
- nAnswer = NEW_ANSWER;
- /* If user entered a valid answer, then exit loop. */
- if(strlen(szNewAnswer) > 0)
- {
- break;
- }
- }
- /* Otherwise, attempt to get answer number from user. */
- nAnswer = atoi(szUserInput) - 1;
- /* If user input is not a valid answer. */
- if(nAnswer < 0 || nAnswer >= QuestionRecord.nTotalAnswers)
- {
- /* Display message. */
- od_printf("That is not a valid response.\n\r");
- WaitForEnter();
- }
- else
- {
- /* Otherwise, exit loop. */
- break;
- }
- }
- /* Add user's vote to question. */
-
- /* Open question file for exclusive access by this node. */
- fpFile = ExclusiveFileOpen(QUESTION_FILENAME, "r+b", &hFile);
- if(fpFile == NULL)
- {
- /* If unable to access file, display error and return. */
- od_printf("Unable to access the question file.\n\r");
- WaitForEnter();
- return;
- }
-
- /* Read the answer record from disk, because it may have been changed. */
- /* by another node. */
- fseek(fpFile, (long)nQuestion * sizeof(tQuestionRecord), SEEK_SET);
- if(fread(&QuestionRecord, sizeof(tQuestionRecord), 1, fpFile) != 1)
- {
- /* If unable to access file, display error and return. */
- ExclusiveFileClose(fpFile, hFile);
- od_printf("Unable to read from question file.\n\r");
- WaitForEnter();
- return;
- }
-
- /* If user entered their own answer, try to add it to the question. */
- if(nAnswer == NEW_ANSWER)
- {
- /* Check that there is still room for another answer. */
- if(QuestionRecord.nTotalAnswers >= MAX_ANSWERS)
- {
- ExclusiveFileClose(fpFile, hFile);
- od_printf("Sorry, this question already has the maximum number of answers.\n\r");
- WaitForEnter();
- return;
- }
-
- /* Set answer number to number of new answer. */
- nAnswer = QuestionRecord.nTotalAnswers;
-
- /* Add 1 to total number of answers. */
- ++QuestionRecord.nTotalAnswers;
-
- /* Initialize new answer string and count. */
- strcpy(QuestionRecord.aszAnswer[nAnswer], szNewAnswer);
- QuestionRecord.auVotesForAnswer[nAnswer] = 0;
- }
-
- /* Add user's vote to question. */
- ++QuestionRecord.auVotesForAnswer[nAnswer];
- ++QuestionRecord.uTotalVotes;
-
- /* Write the question record back to the file. */
- fseek(fpFile, (long)nQuestion * sizeof(tQuestionRecord), SEEK_SET);
- if(fwrite(&QuestionRecord, sizeof(tQuestionRecord), 1, fpFile) != 1)
- {
- /* If unable to access file, display error and return. */
- ExclusiveFileClose(fpFile, hFile);
- od_printf("Unable to write question to file.\n\r");
- WaitForEnter();
- return;
- }
-
- /* Close the question file to allow access by other nodes. */
- ExclusiveFileClose(fpFile, hFile);
-
- /* Record that user has voted on this question. */
- CurrentUserRecord.bVotedOnQuestion[nQuestion] = TRUE;
-
- /* Open user file for exclusive access by this node. */
- fpFile = ExclusiveFileOpen(USER_FILENAME, "r+b", &hFile);
- if(fpFile == NULL)
- {
- /* If unable to access file, display error and return. */
- od_printf("Unable to access the user file.\n\r");
- WaitForEnter();
- return;
- }
- /* Update the user's record in the user file. */
- fseek(fpFile, nCurrentUserNumber * sizeof(tUserRecord), SEEK_SET);
- if(fwrite(&CurrentUserRecord, sizeof(tUserRecord), 1, fpFile) != 1)
- {
- /* If unable to access file, display error and return. */
- ExclusiveFileClose(fpFile, hFile);
- od_printf("Unable to write to user file.\n\r");
- WaitForEnter();
- return;
- }
-
- /* Close the user file to allow access by other nodes. */
- ExclusiveFileClose(fpFile, hFile);
-
- /* Display the result of voting on this question to the user. */
- DisplayQuestionResult(&QuestionRecord);
- /* Add 1 to count of questions that the user has voted on. */
- nQuestionsVotedOn++;
- }
- }
- /* The ViewResults function is called when the user chooses the "view */
- /* results" command from the main menu. This function alows the user to */
- /* choose a question from the list of questions, and then displays the */
- /* results of voting on that question. */
- void ViewResults(void)
- {
- int nChoice;
- tQuestionRecord QuestionRecord;
- int nPageLocation = 0;
- /* Loop until user chooses to return to main menu. */
- for(;;)
- {
- /* Allow the user to choose a question from the list of questions that */
- /* they have already voted on. */
- nChoice = ChooseQuestion(nViewResultsFrom,
- " View Results\n\r", &nPageLocation);
- /* If the user did not choose a question, return to main menu. */
- if(nChoice == NO_QUESTION)
- {
- return;
- }
-
- /* Read the specified question number from the question file. */
- if(!GetQuestion(nChoice, &QuestionRecord))
- {
- return;
- }
-
- /* Display the results for the selected question. */
- DisplayQuestionResult(&QuestionRecord);
- }
- }
- /* The GetQuestion function read the record for the specified question */
- /* number from the question file. */
- int GetQuestion(int nQuestion, tQuestionRecord *pQuestionRecord)
- {
- FILE *fpQuestionFile;
- int hQuestionFile;
- /* Open the question file for exculsive access by this node. */
- fpQuestionFile = ExclusiveFileOpen(QUESTION_FILENAME, "r+b",
- &hQuestionFile);
- if(fpQuestionFile == NULL)
- {
- /* If unable to access file, display error and return. */
- od_printf("Unable to access the question file.\n\r");
- WaitForEnter();
- return(FALSE);
- }
-
- /* Move to location of question in file. */
- fseek(fpQuestionFile, (long)nQuestion * sizeof(tQuestionRecord), SEEK_SET);
-
- /* Read the question from the file. */
- if(fread(pQuestionRecord, sizeof(tQuestionRecord), 1, fpQuestionFile) != 1)
- {
- /* If unable to access file, display error and return. */
- ExclusiveFileClose(fpQuestionFile, hQuestionFile);
- od_printf("Unable to read from question file.\n\r");
- WaitForEnter();
- return(FALSE);;
- }
-
- /* Close the question file to allow access by other nodes. */
- ExclusiveFileClose(fpQuestionFile, hQuestionFile);
- /* Return with success. */
- return(TRUE);
- }
-
- /* The AddQuestion() function is called when the user chooses the "add */
- /* question" option from the main menu. This function allows the user */
- /* to enter a new question, possible responses, and save the question for */
- /* other users to vote on. */
- void AddQuestion(void)
- {
- tQuestionRecord QuestionRecord;
- FILE *fpQuestionFile;
- int hQuestionFile;
- char szLogMessage[100];
- /* Clear the screen. */
- od_clr_scr();
-
- /* Display screen header. */
- od_printf("`bright red` Add A Question\n\r");
- od_printf("`dark red`");
- if(od_control.user_ansi || od_control.user_avatar)
- {
- od_repeat((unsigned char)196, 79);
- }
- else
- {
- od_repeat('-', 79);
- }
- od_printf("\n\r\n\r");
-
- /* Obtain quesiton text from the user. */
- od_printf("`bright green`Enter Your Question (blank line cancels)\n\r");
- od_printf("`dark green`[----------------------------------------------------------------------]\n\r ");
- od_input_str(QuestionRecord.szQuestion, QUESTION_STR_SIZE - 1, ' ', 255);
-
- /* If question was empty, then return to main menu. */
- if(strlen(QuestionRecord.szQuestion) == 0)
- {
- return;
- }
-
- /* Display prompt for answers. */
- od_printf("\n\r`bright green`Enter Possible Answers (blank line when done)\n\r");
- od_printf("`dark green` [------------------------------]\n\r");
-
- /* Loop, getting answers from user. */
- for(QuestionRecord.nTotalAnswers = 0;
- QuestionRecord.nTotalAnswers < MAX_ANSWERS;
- QuestionRecord.nTotalAnswers++)
- {
- /* Display prompt with answer number. */
- od_printf("`bright green`%2d: `dark green`", QuestionRecord.nTotalAnswers + 1);
-
- /* Get string from user. */
- od_input_str(QuestionRecord.aszAnswer[QuestionRecord.nTotalAnswers],
- ANSWER_STR_SIZE - 1, ' ', 255);
-
- /* If string was empty, then exit loop. */
- if(strlen(QuestionRecord.aszAnswer[QuestionRecord.nTotalAnswers]) == 0)
- {
- break;
- }
-
- /* Reset count of votes for this answer to zero. */
- QuestionRecord.auVotesForAnswer[QuestionRecord.nTotalAnswers] = 0;
- }
-
- /* If no answers were supplied, then cancel, returning to main menu. */
- if(QuestionRecord.nTotalAnswers == 0)
- {
- return;
- }
- /* Ask whether users should be able to add their own answers. */
- od_printf("\n\r`bright green`Should voters be able to add their own options? (Y/N) `dark green`");
-
- /* Get answer from user. */
- if(od_get_answer("YN") == 'Y')
- {
- /* If user pressed the 'Y' key. */
- od_printf("Yes\n\r\n\r");
-
- /* Record user's response. */
- QuestionRecord.bCanAddAnswers = TRUE;
- }
- else
- {
- /* If user pressed the 'N' key. */
- od_printf("No\n\r\n\r");
- /* Record user's response. */
- QuestionRecord.bCanAddAnswers = FALSE;
- }
-
- /* Confirm save of new question. */
- od_printf("`bright green`Do you wish to save this new question? (Y/N) `dark green`");
-
- /* If user does not want to save the question, return to main menu now. */
- if(od_get_answer("YN") == 'N')
- {
- return;
- }
- /* Set total number of votes for this question to 0. */
- QuestionRecord.uTotalVotes = 0;
-
- /* Set creator name and creation time for this question. */
- strcpy(QuestionRecord.szCreatorName, od_control.user_name);
- QuestionRecord.lCreationTime = time(NULL);
-
- /* Open question file for exclusive access by this node. */
- fpQuestionFile = ExclusiveFileOpen(QUESTION_FILENAME, "a+b",
- &hQuestionFile);
- if(fpQuestionFile == NULL)
- {
- od_printf("Unable to access the question file.\n\r");
- WaitForEnter();
- return;
- }
-
- /* Determine number of records in question file. */
- fseek(fpQuestionFile, 0, SEEK_END);
-
- /* If question file is full, display message and return to main menu */
- /* after closing file. */
- if(ftell(fpQuestionFile) / sizeof(tQuestionRecord) >= MAX_QUESTIONS)
- {
- ExclusiveFileClose(fpQuestionFile, hQuestionFile);
- od_printf("Cannot add another question, Vote is limited to %d questions.\n\r", MAX_QUESTIONS);
- WaitForEnter();
- return;
- }
-
- /* Add new question to file. */
- if(fwrite(&QuestionRecord, sizeof(QuestionRecord), 1, fpQuestionFile) != 1)
- {
- ExclusiveFileClose(fpQuestionFile, hQuestionFile);
- od_printf("Unable to write to question file.\n\r");
- WaitForEnter();
- return;
- }
-
- /* Close question file, allowing other nodes to access file. */
- ExclusiveFileClose(fpQuestionFile, hQuestionFile);
- /* Record in the logfile that user has added a new question. */
- sprintf(szLogMessage, "User adding questions: %s",
- QuestionRecord.szQuestion);
- od_log_write(szLogMessage);
- }
- /* The ChooseQuestion() function provides a list of questions and allows */
- /* the user to choose a particular question, cancel back to the main menu, */
- /* and page up and down in the list of questions. Depending upon the value */
- /* of the nFromWhichQuestions parameter, this function will present a list */
- /* of questions that the user has voted on, a list of questions that the */
- /* user has not voted on, or a list of all questions. */
- int ChooseQuestion(int nFromWhichQuestions, char *pszTitle, int *nLocation)
- {
- int nCurrent;
- int nFileQuestion = 0;
- int nPagedToQuestion = *nLocation;
- int nDisplayedQuestion = 0;
- char bVotedOnQuestion;
- char chCurrent;
- tQuestionRecord QuestionRecord;
- FILE *fpQuestionFile;
- int hQuestionFile;
- static char szQuestionName[MAX_QUESTIONS][QUESTION_STR_SIZE];
- static int nQuestionNumber[MAX_QUESTIONS];
-
- /* Attempt to open question file. */
- fpQuestionFile = ExclusiveFileOpen(QUESTION_FILENAME, "r+b",
- &hQuestionFile);
- /* If unable to open question file, assume that no questions have been */
- /* created. */
- if(fpQuestionFile == NULL)
- {
- /* Display "no questions yet" message. */
- od_printf("\n\rNo questions have been created so far.\n\r");
-
- /* Wait for user to press enter. */
- WaitForEnter();
-
- /* Indicate that no question has been chosen. */
- return(NO_QUESTION);
- }
-
- /* Loop for every question record in the file. */
- while(fread(&QuestionRecord, sizeof(QuestionRecord), 1, fpQuestionFile) == 1)
- {
- /* Determine whether or not the user has voted on this question. */
- bVotedOnQuestion = CurrentUserRecord.bVotedOnQuestion[nFileQuestion];
-
- /* If this is the kind of question that the user is choosing from */
- /* right now. */
- if((bVotedOnQuestion && (nFromWhichQuestions & QUESTIONS_VOTED_ON)) ||
- (!bVotedOnQuestion && (nFromWhichQuestions & QUESTIONS_NOT_VOTED_ON)))
- {
- /* Add this question to list to be displayed. */
- strcpy(szQuestionName[nDisplayedQuestion],
- QuestionRecord.szQuestion);
- nQuestionNumber[nDisplayedQuestion] = nFileQuestion;
-
- /* Add one to number of questions to be displayed in list. */
- nDisplayedQuestion++;
- }
-
- /* Move to next question in file. */
- ++nFileQuestion;
- }
-
- /* Close question file to allow other nodes to access the file. */
- ExclusiveFileClose(fpQuestionFile, hQuestionFile);
- /* If there are no questions for the user to choose, display an */
- /* appropriate message and return. */
- if(nDisplayedQuestion == 0)
- {
- /* If we were to list all questions. */
- if((nFromWhichQuestions & QUESTIONS_VOTED_ON)
- && (nFromWhichQuestions & QUESTIONS_NOT_VOTED_ON))
- {
- od_printf("\n\rThere are no questions.\n\r");
- }
- /* If we were to list questions that the user has voted on. */
- else if(nFromWhichQuestions & QUESTIONS_VOTED_ON)
- {
- od_printf("\n\rThere are no questions that you have voted on.\n\r");
- }
- /* Otherwise, we were to list questions that use has not voted on. */
- else
- {
- od_printf("\n\rYou have voted on all the questions.\n\r");
- }
-
- /* Wait for user to press enter key. */
- WaitForEnter();
-
- /* Return, indicating that no question was chosen. */
- return(NO_QUESTION);
- }
- /* Ensure that initial paged to location is within range. */
- while(nPagedToQuestion >= nDisplayedQuestion)
- {
- nPagedToQuestion -= QUESTION_PAGE_SIZE;
- }
- /* Loop, displaying current page of questions, until the user makes a */
- /* choice. */
- for(;;)
- {
- /* Clear the screen. */
- od_clr_scr();
- /* Display header. */
- od_printf("`bright red`");
- od_printf(pszTitle);
- od_printf("`dark red`");
- if(od_control.user_ansi || od_control.user_avatar)
- {
- od_repeat((unsigned char)196, 79);
- }
- else
- {
- od_repeat('-', 79);
- }
- od_printf("\n\r");
-
- /* Display list of questions on this page. */
- for(nCurrent = 0;
- nCurrent < QUESTION_PAGE_SIZE
- && nCurrent < (nDisplayedQuestion - nPagedToQuestion);
- ++nCurrent)
- {
- /* Determine character to display for current line. */
- if(nCurrent < 9)
- {
- chCurrent = (char)('1' + nCurrent);
- }
- else
- {
- chCurrent = (char)('A' + (nCurrent - 9));
- }
-
- /* Display this question's title. */
- od_printf("`bright green`%c.`dark green`", chCurrent);
- od_printf(" %s\n\r", szQuestionName[nCurrent + nPagedToQuestion]);
- }
- /* Display prompt for input. */
- od_printf("\n\r`bright white`[Page %d] Choose a question or",
- (nPagedToQuestion / QUESTION_PAGE_SIZE) + 1);
- if(nPagedToQuestion < nDisplayedQuestion - QUESTION_PAGE_SIZE)
- {
- od_printf(" [N]ext page,");
- }
- if(nPagedToQuestion > 0)
- {
- od_printf(" [P]revious page,");
- }
- od_printf(" [Q]uit.\n\r");
-
- /* Loop until the user makes a valid choice. */
- for(;;)
- {
- /* Get input from user */
- chCurrent = (char)od_get_key(TRUE);
- chCurrent = (char)toupper(chCurrent);
-
- /* Respond to user's input. */
-
- /* If user pressed Q key. */
- if(chCurrent == 'Q')
- {
- /* Return without a choosing a question. */
- return(NO_QUESTION);
- }
-
- /* If user pressed P key. */
- else if(chCurrent == 'P')
- {
- /* If we are not at the first page. */
- if(nPagedToQuestion > 0)
- {
- /* Move paged to location up one page. */
- nPagedToQuestion -= QUESTION_PAGE_SIZE;
-
- /* Exit user input loop to display next page. */
- break;
- }
- }
-
- /* If user pressed N key. */
- else if(chCurrent == 'N')
- {
- /* If there is more questions after this page. */
- if(nPagedToQuestion < nDisplayedQuestion - QUESTION_PAGE_SIZE)
- {
- /* Move paged.to location down one page. */
- nPagedToQuestion += QUESTION_PAGE_SIZE;
- /* Exit user input loop to display next page. */
- break;
- }
- }
-
- /* Otherwise, check whether the user chose a valid question. */
- else if ((chCurrent >= '1' && chCurrent <= '9')
- || (chCurrent >= 'A' && chCurrent <= 'H'))
- {
- /* Get question number from key pressed. */
- if(chCurrent >= '1' && chCurrent <= '9')
- {
- nCurrent = chCurrent - '1';
- }
- else
- {
- nCurrent = (chCurrent - 'A') + 9;
- }
-
- /* Add current paged to position to user's choice. */
- nCurrent += nPagedToQuestion;
- /* If this is valid question number. */
- if(nCurrent < nDisplayedQuestion)
- {
- /* Set caller's current question number. */
- *nLocation = nPagedToQuestion;
-
- /* Return actual question number in file. */
- return(nQuestionNumber[nCurrent]);
- }
- }
- }
- }
- }
- /* The DisplayQuestionResult() function is called to display the results */
- /* of voting on a paricular question, and is passed the question record */
- /* of the question. This function is called when the user selects a */
- /* question using the "view results" option, and is also called after */
- /* the user has voted on a question, to display the results of voting on */
- /* that question. */
- void DisplayQuestionResult(tQuestionRecord *pQuestionRecord)
- {
- int nAnswer;
- int uPercent;
- /* Clear the screen. */
- od_clr_scr();
- /* Check that there have been votes on this question. */
- if(pQuestionRecord->uTotalVotes == 0)
- {
- /* If there have been no votes for this question, display a message */
- /* and return. */
- od_printf("Nobody has voted on this question yet.\n\r");
- WaitForEnter();
- return;
- }
- /* Display question itself. */
- od_printf("`bright red`%s\n\r", pQuestionRecord->szQuestion);
- /* Display author's name. */
- od_printf("`dark red`Question created by %s on %s\n\r",
- pQuestionRecord->szCreatorName,
- ctime(&pQuestionRecord->lCreationTime));
-
- /* Display heading for responses. */
- od_printf("`bright green`Response Votes Percent Graph\n\r`dark green`");
- if(od_control.user_ansi || od_control.user_avatar)
- {
- od_repeat((unsigned char)196, 79);
- }
- else
- {
- od_repeat('-', 79);
- }
- od_printf("\n\r");
- /* Loop for each answer to the question. */
- for(nAnswer = 0; nAnswer < pQuestionRecord->nTotalAnswers; ++nAnswer)
- {
- /* Determine percent of users who voted for this answer. */
- uPercent = (pQuestionRecord->auVotesForAnswer[nAnswer] * 100)
- / pQuestionRecord->uTotalVotes;
-
- /* Display answer, total votes and percentage of votes. */
- od_printf("`dark green`%-30.30s %-5u %3u%% `bright white`",
- pQuestionRecord->aszAnswer[nAnswer],
- pQuestionRecord->auVotesForAnswer[nAnswer],
- uPercent);
- /* Display a bar graph corresponding to percent of users who voted */
- /* for this answer. */
- if(od_control.user_ansi || od_control.user_avatar)
- {
- od_repeat((unsigned char)220, (unsigned char)((uPercent * 31) / 100));
- }
- else
- {
- od_repeat('=', (unsigned char)((uPercent * 31) / 100));
- }
- /* Move to next line. */
- od_printf("\n\r");
- }
-
- /* Display footer. */
- od_printf("`dark green`");
- if(od_control.user_ansi || od_control.user_avatar)
- {
- od_repeat((unsigned char)196, 79);
- }
- else
- {
- od_repeat('-', 79);
- }
- od_printf("\n\r");
- od_printf("`dark green` TOTAL: %u\n\r\n\r",
- pQuestionRecord->uTotalVotes);
-
- /* Wait for user to press enter. */
- WaitForEnter();
- }
- /* The ReadOrAddCurrentUser() function is used by Vote to search the */
- /* Vote user file for the record containing information on the user who */
- /* is currently using the door. If this is the first time that the user */
- /* has used this door, then their record will not exist in the user file. */
- /* In this case, this function will add a new record for the current */
- /* user. This function returns TRUE on success, or FALSE on failure. */
- int ReadOrAddCurrentUser(void)
- {
- FILE *fpUserFile;
- int hUserFile;
- int bGotUser = FALSE;
- int nQuestion;
- /* Attempt to open the user file for exclusize access by this node. */
- /* This function will wait up to the pre-set amount of time (as defined */
- /* near the beginning of this file) for access to the user file. */
- fpUserFile = ExclusiveFileOpen(USER_FILENAME, "a+b", &hUserFile);
- /* If unable to open user file, return with failure. */
- if(fpUserFile == NULL)
- {
- return(FALSE);
- }
- /* Begin with the current user record number set to 0. */
- nCurrentUserNumber = 0;
- /* Loop for each record in the file */
- while(fread(&CurrentUserRecord, sizeof(tUserRecord), 1, fpUserFile) == 1)
- {
- /* If name in record matches the current user name ... */
- if(strcmp(CurrentUserRecord.szUserName, od_control.user_name) == 0)
- {
- /* ... then record that we have found the user's record, */
- bGotUser = TRUE;
-
- /* and exit the loop. */
- break;
- }
- /* Move user record number to next user record. */
- nCurrentUserNumber++;
- }
- /* If the user was not found in the file, attempt to add them as a */
- /* new user if the user file is not already full. */
- if(!bGotUser && nCurrentUserNumber < MAX_USERS)
- {
- /* Place the user's name in the current user record. */
- strcpy(CurrentUserRecord.szUserName, od_control.user_name);
-
- /* Record that user hasn't voted on any of the questions. */
- for(nQuestion = 0; nQuestion < MAX_QUESTIONS; ++nQuestion)
- {
- CurrentUserRecord.bVotedOnQuestion[nQuestion] = FALSE;
- }
-
- /* Write the new record to the file. */
- if(fwrite(&CurrentUserRecord, sizeof(tUserRecord), 1, fpUserFile) == 1)
- {
- /* If write succeeded, record that we now have a valid user record. */
- bGotUser = TRUE;
- }
- }
- /* Close the user file to allow other nodes to access it. */
- ExclusiveFileClose(fpUserFile, hUserFile);
- /* Return, indciating whether or not a valid user record now exists for */
- /* the user that is currently online. */
- return(bGotUser);
- }
- /* The WriteCurrentUser() function is called to save the information on the */
- /* user who is currently using the door, to the VOTE.USR file. */
- void WriteCurrentUser(void)
- {
- FILE *fpUserFile;
- int hUserFile;
- /* Attempt to open the user file for exclusize access by this node. */
- /* This function will wait up to the pre-set amount of time (as defined */
- /* near the beginning of this file) for access to the user file. */
- fpUserFile = ExclusiveFileOpen(USER_FILENAME, "r+b", &hUserFile);
- /* If unable to access the user file, display an error message and */
- /* return. */
- if(fpUserFile == NULL)
- {
- od_printf("Unable to access the user file.\n\r");
- WaitForEnter();
- return;
- }
-
- /* Move to appropriate location in user file for the current user's */
- /* record. */
- fseek(fpUserFile, (long)nCurrentUserNumber * sizeof(tUserRecord), SEEK_SET);
- /* Write the new record to the file. */
- if(fwrite(&CurrentUserRecord, sizeof(tUserRecord), 1, fpUserFile) == 1)
- {
- /* If unable to write the record, display an error message. */
- ExclusiveFileClose(fpUserFile, hUserFile);
- od_printf("Unable to update your user record file.\n\r");
- WaitForEnter();
- return;
- }
-
- /* Close the user file to allow other nodes to access it again. */
- ExclusiveFileClose(fpUserFile, hUserFile);
- }
- /* This function is used by Vote to open a file. If Vote has been compiled */
- /* with #define MULTINODE_AWARE uncommented (see the beginning of this */
- /* file), file access is performed in a multinode-aware way. This implies */
- /* that the file is opened of exclusive access, using share-aware open */
- /* functions that may not be available using all compilers. */
- FILE *ExclusiveFileOpen(char *pszFileName, char *pszMode, int *phHandle)
- {
- #ifdef MULTINODE_AWARE
- /* If Vote is being compiled for multinode-aware file access, then */
- /* attempt to use compiler-specific share-aware file open functions. */
- FILE *fpFile = NULL;
- time_t StartTime = time(NULL);
- int hFile;
- /* Attempt to open the file while there is still time remaining. */
- while((hFile = sopen(pszFileName, O_BINARY | O_RDWR, SH_DENYRW,
- S_IREAD | S_IWRITE)) == -1)
- {
- /* If we have been unable to open the file for more than the */
- /* maximum wait time, or if open failed for a reason other */
- /* than file access, then attempt to create a new file and */
- /* exit the loop. */
- if(errno != EACCES ||
- difftime(time(NULL), StartTime) >= FILE_ACCESS_MAX_WAIT)
- {
- hFile = sopen(pszFileName, O_BINARY | O_CREAT, SH_DENYRW,
- S_IREAD | S_IWRITE);
- break;
- }
- /* If we were unable to open the file, call od_kernel, so that */
- /* OpenDoors can continue to respond to sysop function keys, loss */
- /* of connection, etc. */
- od_kernel();
- }
- /* Attempt to obtain a FILE * corresponding to the handle. */
- if(hFile != -1)
- {
- fpFile = fdopen(hFile, pszMode);
- if(fpFile == NULL)
- {
- close(hFile);
- }
- }
- /* Pass file handle back to the caller. */
- *phHandle = hFile;
- /* Return FILE pointer for opened file, if any. */
- return(fpFile);
- #else
- /* Ignore unused parameters. */
- (void)phHandle;
- /* If Vote is not being compiled for multinode-aware mode, then just */
- /* use fopen to access the file. */
- return(fopen(pszFileName, pszMode));
- #endif
- }
- /* The ExclusiveFileClose() function closes a file that was opened using */
- /* ExclusiveFileOpen(). */
- void ExclusiveFileClose(FILE *pfFile, int hHandle)
- {
- fclose(pfFile);
- #ifdef MULTINODE_AWARE
- close(hHandle);
- #else
- /* Ignore unused parameters. */
- (void)hHandle;
- #endif
- }
- /* The WaitForEnter() function is used by Vote to create its custom */
- /* "Press [ENTER] to continue." prompt. */
- void WaitForEnter(void)
- {
- /* Display prompt. */
- od_printf("`bright white`Press [ENTER] to continue.\n\r");
-
- /* Wait for a Carriage Return or Line Feed character from the user. */
- od_get_answer("\n\r");
- }
|