home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / top2src.zip / BIO.C < prev    next >
C/C++ Source or Header  |  2000-07-13  |  22KB  |  616 lines

  1. /******************************************************************************
  2. BIO.C        Biography functions.
  3.  
  4.     Copyright 1993 - 2000 Paul J. Sidorsky
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License, version 2, as
  8.     published by the Free Software Foundation.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  
  19. This module contains all functions that pertain to the Biography feature of
  20. TOP.
  21. ******************************************************************************/
  22.  
  23. #include "top.h"
  24.  
  25. /* loadbioquestions() - Load biography questions from BIOQUES.CFG.
  26.    Parameters:  None.
  27.    Returns:  TRUE on success, FALSE on error.
  28. */
  29. char loadbioquestions(void)
  30.     {
  31.     FILE *bfil = NULL; /* File stream for cfg file. */
  32.     XINT qcount = 0; /* Question count. */
  33.  
  34.     /* Open configuration file. */
  35.     sprintf(outbuf, "%sBIOQUES.CFG", cfg.toppath);
  36.     bfil = fopen(outbuf, "rt");
  37.     if (bfil == NULL)
  38.         {
  39.         return 0;
  40.         }
  41.  
  42.     /* Read and process each line. */
  43.     while (!feof(bfil))
  44.         {
  45.         fgets(outbuf, 256, bfil);
  46.         /* Ignore comments. */
  47.         if (outbuf[0] == ';')
  48.             {
  49.             continue;
  50.             }
  51.         stripcr(outbuf);
  52.         memset(&bioques[qcount], 0, sizeof(bio_ques_typ));
  53.         /* Parse the line.  See BIOQUES.CFG notes for format description. */
  54.         bioques[qcount].number = atoi(&outbuf[0]);
  55.         strncpy(bioques[qcount].field, &outbuf[3], 30);
  56.         trim_string(bioques[qcount].field, bioques[qcount].field,
  57.                     NO_LTRIM);
  58.         if (!strnicmp(&outbuf[34], "NUM", 3))
  59.             bioques[qcount].resptype = BIORESP_NUMERIC;
  60.         if (!strnicmp(&outbuf[34], "STR", 3))
  61.             bioques[qcount].resptype = BIORESP_STRING;
  62.         bioques[qcount].minval = atol(&outbuf[38]);
  63.         bioques[qcount].maxval = atol(&outbuf[50]);
  64.         qcount++;
  65.         }
  66.  
  67.     fclose(bfil);
  68.  
  69.     numbioques = qcount;
  70.  
  71.     return 1;
  72.     }
  73.  
  74. /* displaybio - Displays a user's biography.
  75.    Parameters:  uname - String naming the user who's biography is to be
  76.                         displayed.
  77.    Returns:  TRUE if the bio was found and shown, FALSE on error.
  78.    Notes:  Currently, the user's FULL name is required.  Partial matching
  79.            is not supported.
  80. */
  81. char displaybio(unsigned char *uname)
  82.     {
  83.     long resploc[MAXBIOQUES]; /* Response locations. */
  84.     XINT unum = -1, d; /* User record number, counter. */
  85.     bio_resp_typ rbuf; /* Bio response buffer. */
  86.     user_data_typ ubuf; /* User data buffer. */
  87.     char ns = 0; /* Nonstop mode flag. */
  88.  
  89.     /* Locate the user and determine if the user's bio has been completed. */
  90.     unum = find_user_from_name(uname, &ubuf, UFIND_USECFG);
  91.     if (unum != user_rec_num && !ubuf.donebio)
  92.         {
  93.         top_output(OUT_SCREEN, getlang("BioUserHasNone"));
  94.         return 0;
  95.         }
  96.     if (unum < 0)
  97.         {
  98.         fixname(uname, uname);
  99.         top_output(OUT_SCREEN, getlang("BioUserNotFound"), uname);
  100.         return 0;
  101.         }
  102.  
  103.     /* Load the biography repsonse index for this user from BIOIDX.TOP. */
  104.     rec_locking(REC_LOCK, bioidxfil,
  105.                 (long) unum * (MAXBIOQUES * sizeof(long)),
  106.                 MAXBIOQUES * sizeof(long));
  107.     lseek(bioidxfil, (long) unum * (MAXBIOQUES * sizeof(long)), SEEK_SET);
  108.     read(bioidxfil, resploc, MAXBIOQUES * sizeof(long));
  109.     rec_locking(REC_UNLOCK, bioidxfil,
  110.                 (long) unum * (MAXBIOQUES * sizeof(long)),
  111.                 MAXBIOQUES * sizeof(long));
  112.  
  113.     /* Response numbers are stored as 1-based, with 0 indicating that no
  114.        response is present.  This loop converts the numbers to 0-based. */
  115.     for (d = 0; d < MAXBIOQUES; d++, resploc[d]--);
  116.  
  117.     top_output(OUT_SCREEN, getlang("BioDispPrefix"), ubuf.handle);
  118.  
  119.     for (d = 0; d < numbioques; d++)
  120.         {
  121.         itoa(bioques[d].number, outnum[0], 10);
  122.         if (unum == user_rec_num)
  123.             {
  124.             /* If we're displaying our own biography, show the question number
  125.                as well because the function may have been called from the
  126.                editor. */
  127.             top_output(OUT_SCREEN, getlang("BioQuesNumber"), outnum[0]);
  128.             }
  129.         top_output(OUT_SCREEN, getlang("BioRespPrefix"), bioques[d].field);
  130.  
  131.         if (resploc[bioques[d].number] >= 0)
  132.             {
  133.             /* Load the response for the question. */
  134.             rec_locking(REC_LOCK, biorespfil,
  135.                         resploc[bioques[d].number] * sizeof(bio_resp_typ),
  136.                         sizeof(bio_resp_typ));
  137.             lseek(biorespfil,
  138.                   resploc[bioques[d].number] * sizeof(bio_resp_typ),
  139.                   SEEK_SET);
  140.             read(biorespfil, &rbuf, sizeof(bio_resp_typ));
  141.             rec_locking(REC_UNLOCK, biorespfil,
  142.                         resploc[bioques[d].number] * sizeof(bio_resp_typ),
  143.                         sizeof(bio_resp_typ));
  144.  
  145.             top_output(OUT_SCREEN, getlang("BioResp"), rbuf.response);
  146.             }
  147.         else
  148.             {
  149.             top_output(OUT_SCREEN, getlang("BioNoResp"));
  150.             }
  151.         top_output(OUT_SCREEN, getlang("BioRespSuffix"));
  152.         /* Display more prompt every 23 lines. */
  153.         if (d % 23 == 0 && d > 0 && !ns)
  154.             {
  155.             ns = moreprompt();
  156.             if (ns == -1)
  157.                 {
  158.                 break;
  159.                 }
  160.             }
  161.         }
  162.  
  163.     top_output(OUT_SCREEN, getlang("BioDispSuffix"));
  164.  
  165.     return 1;
  166.     }
  167.  
  168. /* writebioresponse() - Writes a biography response to disk.
  169.    Parameters:  qnum - Question number of the response.
  170.                 br - Pointer to response data.
  171.    Returns:  TRUE on success, FALSE on failure.
  172. */
  173. char writebioresponse(XINT qnum, bio_resp_typ *br)
  174.     {
  175.     long rnum = 0; /* Response number. */
  176.     char newresp = 0; /* Whether or not this is a new response. */
  177.  
  178.     /* Load the current value for this question from BIOIDX.TOP to see if a
  179.        response to this question already exists. */
  180.     rec_locking(REC_LOCK, bioidxfil,
  181.                 (user_rec_num * (MAXBIOQUES * sizeof(long))) +
  182.                 ((long) qnum * sizeof(long)), sizeof(long));
  183.     lseek(bioidxfil,
  184.          (user_rec_num * (MAXBIOQUES * sizeof(long))) +
  185.          ((long) qnum * sizeof(long)), SEEK_SET);
  186.     read(bioidxfil, &rnum, sizeof(long));
  187.     if (rnum <= 0)
  188.         {
  189.         /* Obtain the next question number. */
  190.         rnum = (filelength(biorespfil) / sizeof(bio_resp_typ)) + 1L;
  191.         newresp = 1;
  192.         }
  193.  
  194.     /* Write the response. */
  195.     rec_locking(REC_LOCK, biorespfil, (rnum - 1L) * sizeof(bio_resp_typ),
  196.                 sizeof(bio_resp_typ));
  197.     lseek(biorespfil, (rnum - 1L) * sizeof(bio_resp_typ), SEEK_SET);
  198.     write(biorespfil, br, sizeof(bio_resp_typ));
  199.     rec_locking(REC_UNLOCK, biorespfil, (rnum - 1L) * sizeof(bio_resp_typ),
  200.                 sizeof(bio_resp_typ));
  201.  
  202.     /* If this is a new response, write the location to the index. */
  203.     if (newresp)
  204.         {
  205.         lseek(bioidxfil,
  206.              (user_rec_num * (MAXBIOQUES * sizeof(long))) +
  207.              ((long) qnum * sizeof(long)), SEEK_SET);
  208.         write(bioidxfil, &rnum, sizeof(long));
  209.         }
  210.     rec_locking(REC_UNLOCK, bioidxfil,
  211.                 (user_rec_num * (MAXBIOQUES * sizeof(long))) +
  212.                 ((long) qnum * sizeof(long)), sizeof(long));
  213.  
  214.     return 1;
  215.     }
  216.  
  217. /* readbioresponse() - Reads a biography response from disk.
  218.    Parameters:  qnum - Question number of the response.
  219.                 br - Pointer to response buffer to fill.
  220.    Returns:  TRUE on success, FALSE on failure.
  221. */
  222. char readbioresponse(XINT qnum, bio_resp_typ *br)
  223.     {
  224.     long rnum; /* Response number. */
  225.  
  226.     /* Obtain the response number for this question from BIOIDX.TOP. */
  227.     rec_locking(REC_LOCK, bioidxfil,
  228.                 (user_rec_num * (MAXBIOQUES * sizeof(long))) +
  229.                 ((long) qnum * sizeof(long)), sizeof(long));
  230.     lseek(bioidxfil,
  231.          (user_rec_num * (MAXBIOQUES * sizeof(long))) +
  232.          ((long) qnum * sizeof(long)), SEEK_SET);
  233.     read(bioidxfil, &rnum, sizeof(long));
  234.     /* Abort if the index indicates no response is present. */
  235.     if (rnum <= 0)
  236.         {
  237.         rec_locking(REC_UNLOCK, bioidxfil,
  238.                     (user_rec_num * (MAXBIOQUES * sizeof(long))) +
  239.                     ((long) qnum * sizeof(long)), sizeof(long));
  240.         return 0;
  241.         }
  242.  
  243.     /* Convert response number to 0-based. */
  244.     rnum--;
  245.  
  246.     /* Load the response itself. */
  247.     rec_locking(REC_LOCK, biorespfil, rnum * sizeof(bio_resp_typ),
  248.                 sizeof(bio_resp_typ));
  249.     lseek(biorespfil, rnum * sizeof(bio_resp_typ), SEEK_SET);
  250.     read(biorespfil, br, sizeof(bio_resp_typ));
  251.     rec_locking(REC_UNLOCK, biorespfil, rnum * sizeof(bio_resp_typ),
  252.                 sizeof(bio_resp_typ));
  253.  
  254.     rec_locking(REC_UNLOCK, bioidxfil,
  255.                 (user_rec_num * (MAXBIOQUES * sizeof(long))) +
  256.                 ((long) qnum * sizeof(long)), sizeof(long));
  257.  
  258.     return 1;
  259.     }
  260.  
  261. /* getbioresponse() - Prompts the user for a biography response.
  262.    Parameters:  rec - Record number of question the user is responding to.
  263.                 br - Pointer to buffer to hold the response.
  264.    Returns:  TRUE if the response changed, FALSE if the user aborted.
  265. */
  266. char getbioresponse(XINT rec, bio_resp_typ *br)
  267.     {
  268.     char tresp[256]; /* Input buffer. */
  269.     long tnr; /* Response length holder. */
  270.     char doneresp = 0, changed = 0; /* Loop abort flag, changed flag. */
  271.  
  272.     /* Loop until the user enters a valid response. */
  273.     do
  274.         {
  275.         if (bioques[rec].resptype == BIORESP_NUMERIC)
  276.             {
  277.             /* Prompt the user. */
  278.             itoa(bioques[rec].number, outnum[0], 10);
  279.             top_output(OUT_SCREEN, getlang("BioNumRespPrompt"), outnum[0],
  280.                        bioques[rec].field);
  281.  
  282. //            od_input_str(tresp,
  283. //                         numresplen(bioques[rec].minval, bioques[rec].maxval),
  284. //                         '-', '9');
  285.             /* Get a string, restricting the user to numeric values only. */
  286.             od_input_str(tresp, 11, '-', '9');
  287.             tnr = strtol(tresp, NULL, 10);
  288.             if (tnr < bioques[rec].minval ||
  289.                 tnr > bioques[rec].maxval &&
  290.                 tresp[0])
  291.                 {
  292.                 /* Abort if the value is too low or high, but only if
  293.                    something was entered. */
  294.                 ltoa(bioques[rec].minval, outnum[0], 10);
  295.                 ltoa(bioques[rec].maxval, outnum[1], 10);
  296.                 top_output(OUT_SCREEN, getlang("BioBadNumResp"), outnum[0],
  297.                            outnum[1]);
  298.                 }
  299.             else
  300.                 {
  301.                 /* If the user entered something, set the changed flag. */
  302.                 if (tresp[0])
  303.                     {
  304.                     /* All responses are stored as strings, even for
  305.                        numeric questions. */
  306.                     ltoa(tnr, br->response, 10);
  307.                     changed = 1;
  308.                     }
  309.                 doneresp = 1;
  310.                 }
  311.             }
  312.         if (bioques[rec].resptype == BIORESP_STRING)
  313.             {
  314.             /* Loop until the censor accepts the input. */
  315.             do
  316.                 {
  317.                 itoa(bioques[rec].number, outnum[0], 10);
  318.                 top_output(OUT_SCREEN, getlang("BioStrRespPrompt"), outnum[0],
  319.                            bioques[rec].field);
  320.  
  321.                 od_input_str(tresp, bioques[rec].maxval, ' ',
  322.                              MAXASCII);
  323.                 }
  324.             while(censorinput(tresp));
  325.  
  326.             if (strlen(tresp) < bioques[rec].minval)
  327.                 {
  328.                 /* Reject the response if it was too short. */
  329.                 itoa(bioques[rec].minval, outnum[0], 10);
  330.                 top_output(OUT_SCREEN, getlang("BioBadStrResp"), outnum[0]);
  331.                 }
  332.             else
  333.                 {
  334.                 /* If the user entered something, set the changed flag. */
  335.                 if (tresp[0])
  336.                     {
  337.                     strcpy(br->response, tresp);
  338.                     changed = 1;
  339.                     }
  340.                 doneresp = 1;
  341.                 }
  342.             }
  343.         }
  344.     while (!doneresp);
  345.  
  346.     top_output(OUT_SCREEN, getlang("BioRespSuffix"));
  347.  
  348.     return changed;
  349.     }
  350.  
  351. /* biogetall() - Asks the user all of the biography questions.
  352.    Parameters:  None.
  353.    Returns:  The number of questions the user has filled out, including any
  354.              new responses.
  355. */
  356. char biogetall(void)
  357.     {
  358.     XINT d; /* Counter. */
  359.     bio_resp_typ tresp; /* Response buffer. */
  360.     char chg = 0; /* Flag indicating at least one new response. */
  361.     char qc = 0; /* Question count. */
  362.  
  363.     top_output(OUT_SCREEN, getlang("BioGetAllPrefix"));
  364.  
  365.     for (d = 0; d < numbioques; d++)
  366.         {
  367.         if (!readbioresponse(bioques[d].number, &tresp))
  368.             {
  369.             /* Only ask for a response if one is not found. */
  370.             if (getbioresponse(d, &tresp))
  371.                 {
  372.                 /* Write the new response if one was entered. */
  373.                 chg = 1;
  374.                 qc++;
  375.                 writebioresponse(bioques[d].number, &tresp);
  376.                 if (!user.donebio)
  377.                     {
  378.                     /* Flag that the user has filled out at least one
  379.                        Bio question. */
  380.                     user.donebio = 1;
  381.                     save_user_data(user_rec_num, &user);
  382.                     }
  383.                 }
  384.             }
  385.         else
  386.             {
  387.             /* Responses found on disk are counted. */
  388.             qc++;
  389.             }
  390.         }
  391.  
  392.     /* Notify other users if the biography was changed. */
  393.     if (chg)
  394.         {
  395.         curchannel = cmiprevchan;
  396.         dispatch_message(MSG_BIOCHG, "\0", -1, 0, 0);
  397.         curchannel = BUSYCHANNEL;
  398.         }
  399.  
  400.     top_output(OUT_SCREEN, getlang("BioGetAllSuffix"));
  401.  
  402.     return qc;
  403.     }
  404.  
  405. /* bioeditor() - Interactive biography editor.
  406.    Parameters:  None.
  407.    Returns:  Nothing.
  408. */
  409. void bioeditor(void)
  410.     {
  411.     char tbuf[3]; /* Input buffer. */
  412.     XINT d, nn; /* Counter, number of question to edit. */
  413.     bio_resp_typ tresp; /* Response buffer. */
  414.     char chg = 0; /* Flag indicating bio was changed. */
  415.  
  416.     /* If the user has not filled out at least one question, invite the
  417.        user to fill out all questions. */
  418.     if (!user.donebio)
  419.         {
  420.         top_output(OUT_SCREEN, getlang("BioEditDoAllPrompt"));
  421.         d = toupper(od_get_key(TRUE));
  422.         top_output(OUT_SCREEN, getlang("BioEditDoAllSuffix"));
  423.         // This should use YesNoKeys...
  424.         if (d != 'N')
  425.             {
  426.             biogetall();
  427.             }
  428.         }
  429.  
  430.     displaybio(user.handle);
  431.     for(;;)
  432.         {
  433.         /* Get the question number. */
  434.         top_output(OUT_SCREEN, getlang("BioEditPrompt"));
  435.         od_input_str(tbuf, 2, '0', 'z');
  436.         strupr(tbuf);
  437.         if (!tbuf[0])
  438.             {
  439.             continue;
  440.             }
  441.         /* Quit editor. */
  442.         if (strchr(getlang("BioEditQuitKeys"), tbuf[0]))
  443.             {
  444.             top_output(OUT_SCREEN, getlang("BioEditSuffix"));
  445.  
  446.             /* Notify other users if the biography was changed. */
  447.             if (chg)
  448.                 {
  449.                 curchannel = cmiprevchan;
  450.                 dispatch_message(MSG_BIOCHG, "\0", -1, 0, 0);
  451.                 curchannel = BUSYCHANNEL;
  452.                 }
  453.             /* Break out of the infinite loop. */
  454.             return;
  455.             }
  456.         /* Redisplay biography. */
  457.         if (strchr(getlang("BioEditShowKeys"), tbuf[0]))
  458.             {
  459.             displaybio(user.handle);
  460.             continue;
  461.             }
  462.         /* Complete all unanswered questions. */
  463.         if (strchr(getlang("BioEditCompKeys"), tbuf[0]))
  464.             {
  465.             biogetall();
  466.             displaybio(user.handle);
  467.             }
  468.         nn = atoi(tbuf);
  469.         for (d = 0; d < numbioques; d++)
  470.             {
  471.             /* Find the question number in memory. */
  472.             if (nn == bioques[d].number)
  473.                 {
  474.                 /* Get a response. */
  475.                 if (getbioresponse(d, &tresp))
  476.                     {
  477.                     chg = 1;
  478.                     writebioresponse(bioques[d].number, &tresp);
  479.                     if (!user.donebio)
  480.                         {
  481.                         /* Flag that the user has filled out at least one
  482.                            Bio question. */
  483.                         user.donebio = 1;
  484.                         save_user_data(user_rec_num, &user);
  485.                         }
  486.                     }
  487.                 }
  488.             }
  489.         if (nn < 0 || nn >= MAXBIOQUES)
  490.             {
  491.             top_output(OUT_SCREEN, getlang("BioEditBadQNum"));
  492.             }
  493.         }
  494.  
  495.     }
  496.  
  497. /* userlist() - Displays a list of user names and summaries.
  498.    Parameters:  None.
  499.    Returns:  Nothing.
  500.    Notes:  This function may seem out of place here, but because it
  501.            accesses the biographies and wasn't added until the biography
  502.            feature was implemented, I've placed it in this module.
  503. */
  504. void userlist(void)
  505.     {
  506.     bio_resp_typ sum; /* Buffer to hold the summary response. */
  507.     XINT d, numus, x = 0; /* Counter, number of users, line counter. */
  508.     user_data_typ ubuf; /* User data buffer. */
  509.     long sloc; /* Location of summary. */
  510.     char ns = 0; /* Nonstop mode flag. */
  511.     XINT qrn = -1; /* Record number (in memory) of Summary question. */
  512.  
  513.     /* Question #99 is considered the "summary" question, and is shown
  514.        beside each user name if a response exists.  This loop determines
  515.        if a question #99 exists and finds it in memory. */
  516.     for (d = 0; d < numbioques && qrn == -1; d++)
  517.         {
  518.         if (bioques[d].number == 99)
  519.             {
  520.             qrn = d;
  521.             }
  522.         }
  523.  
  524.     /* Prepare screen. */
  525.     top_output(OUT_SCREEN, getlang("UserListHeader"),
  526.                qrn > -1 ? bioques[qrn].field : "");
  527.     top_output(OUT_SCREEN, getlang("UserListSep"));
  528.  
  529.     numus = filelength(userfil) / sizeof(user_data_typ);
  530.     for (d = 0; d < numus; d++)
  531.         {
  532.         /* Load just the handle and name of each user, for speed. */
  533.         rec_locking(REC_LOCK, userfil, (long) d * sizeof(user_data_typ),
  534.                     72L);
  535.         lseek(userfil, (long) d * sizeof(user_data_typ), SEEK_SET);
  536.         read(userfil, &ubuf, 72L);
  537.         rec_locking(REC_UNLOCK, userfil, (long) d * sizeof(user_data_typ),
  538.                     72L);
  539.  
  540.         /* Load the location of the user's response to question #99 from
  541.            the index. */
  542.         rec_locking(REC_LOCK, bioidxfil,
  543.                     ((long) d * MAXBIOQUES * sizeof(long)) +
  544.                     (99L * sizeof(long)),
  545.                     sizeof(long));
  546.         lseek(bioidxfil, ((long) d * MAXBIOQUES * sizeof(long)) +
  547.                          (99L * sizeof(long)), SEEK_SET);
  548.         read(bioidxfil, &sloc, sizeof(long));
  549.         rec_locking(REC_UNLOCK, bioidxfil,
  550.                     ((long) d * MAXBIOQUES * sizeof(long)) +
  551.                     (99L * sizeof(long)),
  552.                     sizeof(long));
  553.  
  554.         sum.response[0] = '\0';
  555.         /* Only try to load the response if a question #99 exists. */
  556.         if (qrn > -1)
  557.             {
  558.             /* Load the response if one was found. */
  559.             if (sloc > 0)
  560.                 {
  561.                 rec_locking(REC_LOCK, biorespfil,
  562.                             (sloc - 1) * sizeof(bio_resp_typ),
  563.                             sizeof(bio_resp_typ));
  564.                 lseek(biorespfil, (sloc - 1) * sizeof(bio_resp_typ),
  565.                       SEEK_SET);
  566.                 read(biorespfil, &sum, sizeof(bio_resp_typ));
  567.                 rec_locking(REC_UNLOCK, biorespfil,
  568.                             (sloc - 1) * sizeof(bio_resp_typ),
  569.                             sizeof(bio_resp_typ));
  570.                 }
  571.             }
  572.  
  573.         /* Show the entry. */
  574.         top_output(OUT_SCREEN, getlang("UserListEntry"),
  575.                    ubuf.handle, sum.response);
  576.         /* Show a more prompt every 20 users. */
  577.         if (++x % 20 == 0 && x > 0 && !ns)
  578.             {
  579.             x = 0;
  580.             ns = moreprompt();
  581.             if (ns == -1)
  582.                 {
  583.                 break;
  584.                 }
  585.             }
  586.         }
  587.  
  588.     top_output(OUT_SCREEN, getlang("UserListSuffix"));
  589.  
  590.     }
  591.  
  592. /* biocheckalldone() - Checks if a user has completed all bio questions.
  593.    Parameters:  None.
  594.    Returns:  TRUE if all bio questions have been completed, FALSE otherwise.
  595.    Notes:  Used when the ForceBio configuration option is turned on.
  596. */
  597. char biocheckalldone(void)
  598.     {
  599.     XINT d; /* Counter. */
  600.     bio_resp_typ tresp; /* Response buffer. */
  601.  
  602.     /* Cycle through each question. */
  603.     for (d = 0; d < numbioques; d++)
  604.         {
  605.         /* Abort with failure if no response can be loaded. */
  606.         if (!readbioresponse(bioques[d].number, &tresp))
  607.             {
  608.             return 0;
  609.             }
  610.         }
  611.  
  612.     /* Successful completion of the loop indicates all questions have been
  613.        completed by the user. */
  614.     return 1;
  615.     }
  616.