home *** CD-ROM | disk | FTP | other *** search
/ Solo Programadores 22 / SOLO_22.iso / docs / lovelace / mklesson < prev    next >
Encoding:
Text File  |  1995-11-21  |  24.1 KB  |  720 lines

  1. #!/usr/local/bin/perl -s
  2. # This is mklesson, a `lesson compiler.'
  3. # Mklesson takes a lesson file (.les) as its sole argument, and uses it to
  4. # generate a set of hypertext tutor files in HTML (.html) format.
  5. # The generated files are created in the current directory.
  6. # Status information is sent to standard output (which is usually the screen).
  7. #
  8. # Usage Example:
  9. #     mklesson lesson9.les
  10. #
  11. # $Id: mklesson,v 1.15 1995/08/09 21:07:40 wheeler Exp $
  12. #
  13. # See the user's guide in file userg.html and formatmk.txt.
  14. #
  15. # Input:
  16. #   (argument 1) -- filename of lesson (.les) file.
  17. #   template -- file with templates used for file generation.
  18. #   default  -- if this file exists, it is read to find default settings
  19. #               (its format is identical to a lesson header section).
  20. #
  21. # Output: The program generates files with the following naming patterns:
  22. #    sLESSON-SECTION.html  - text for lesson number LESSON, section SECTION.
  23. #                            If this is the last one, SECTION is "f" (final)
  24. #                            so that the next lesson can always link
  25. #                            backwards correctly (didn't use "l" for last,
  26. #                            because it looks too much like a "1").
  27. #    sLESSON-SECTIONrRESPONSENUMBER.html - text for response RESPONSENUMBER
  28. #    lessonLESSON.html    - lesson outline.
  29. #
  30. #
  31. # This program is written in the programming language perl because it's
  32. # a relatively short program that performs text processing (a perl strength),
  33. # and I wanted it to be VERY portable (perl is widely available).
  34. # Perl has some disadvantages, in particular, please be careful modifying
  35. # this program because the Perl language syntax is so awful that it's
  36. # as though it was designed to maximize error creation.
  37. #
  38. # Copyright (C) 1994 David A. Wheeler.
  39. #
  40. #    This program is free software; you can redistribute it and/or modify
  41. #    it under the terms of the GNU General Public License as published by
  42. #    the Free Software Foundation; either version 2 of the License, or
  43. #    (at your option) any later version.
  44. #
  45. #    This program is distributed in the hope that it will be useful,
  46. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  47. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  48. #    GNU General Public License for more details.
  49. #
  50. #    You should have received a copy of the GNU General Public License
  51. #    along with this program; if not, write to the Free Software
  52. #    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  53. #
  54. # Since mklesson is free software, if you modify this program and
  55. # then distribute it (or results generated from it) you ARE REQUIRED
  56. # to distribute & permit anyone to copy the modified version of this program.
  57. #
  58. # You CAN use this program to generate
  59. # proprietary tutorials, just as you can use gcc to generate
  60. # proprietary executables. See the GPL for more information.
  61.  
  62.  
  63. # START OF MAIN PROGRAM
  64.  
  65. if (defined($s)) {
  66.    # a "-s" option uses Short extension names (.htm).
  67.    $html_extension = ".htm";
  68. }
  69. else {
  70.    # without a "-s" option, generate ".html" extensions.
  71.    $html_extension = ".html";
  72. }
  73.  
  74. &initialize_variables;
  75. &read_in_template;  # Load the template file into memory.
  76. &read_in_defaults;  # Load the contents of file "default".
  77.  
  78. # Open & process the lesson file.
  79. $lesson_source=shift;
  80. &open_lesson_file;
  81. &process_input_header;
  82. &process_input_body;
  83. &change_timestamp_file($lesson_source);
  84.  
  85. &move_in_new_files;
  86.  
  87. # END OF MAIN PROGRAM
  88.  
  89.  
  90. # SUBROUTINES:
  91.  
  92. sub process_input_header {
  93.  # Process input file as a lesson header section.
  94.  # Stop processing at end-of-file or a <SECTION> marker.
  95.  # (The input file could be a "lesson" file or "default" file)
  96.  
  97.  while (&read_line) {
  98.   if (m/^ *<TUTOR .*NAME="([^"]*)"/i) {
  99.     if (($tutor_name ne "") && ($tutor_name ne $1) )
  100.        {die "Cannot redefine tutor name"};
  101.     $tutor_name=$1;
  102.     print "Tutor name is $tutor_name\n";
  103.     }
  104.   elsif (m/^ *<LESSON .*NUMBER=([0-9]+)/i) {
  105.    if ($lesson_number != 0) {die "Cannot redefine lesson number"};
  106.    $lesson_number=$1;
  107.    $lesson_outline_URL = "lesson${lesson_number}" . $html_extension;
  108.    print "Lesson number is $lesson_number\n";
  109.    }
  110.   elsif (m/^ *<AUTHOR .*NAME="([^"]*)".*EMAIL="([^"]*)"/i) {
  111.    $author_name=$1;
  112.    $author_email=$2;
  113.    print "Author name is $author_name\n";
  114.    print "Author email (address) is $author_email\n";
  115.    }
  116.   elsif (m/^ *<AUTHOR .*ADDRESS="(.*)">$/i) {
  117.    # Note: this format is slightly different, grabbing the ENTIRE line.
  118.    # This is so that the address can include anchors and such, which
  119.    # require the use of " and > characters.
  120.    $author_address=$1;
  121.    print "Author address is $author_address\n";
  122.    }
  123.   elsif (m/^ *<PREVIOUS_LESSON .*LOCATION="([^"]*)"/i) {
  124.    if ($previous_lesson_location_set != 0)
  125.        {die "Cannot redefine previous lesson location"};
  126.    $previous_lesson_location = $1;
  127.    $previous_lesson_location_set = 1;
  128.    print "Previous lesson location is $previous_lesson_location\n";
  129.    }
  130.   elsif (m/^ *<NEXT_LESSON .*LOCATION="([^"]*)"/i) {
  131.    if ($next_lesson_location_set != 0)
  132.       {die "Cannot redefine next lesson location"};
  133.    $next_lesson_location = $1;
  134.    $next_lesson_location_set = 1;
  135.    print "Next lesson location is $next_lesson_location\n";
  136.    }
  137.   elsif (m/^ *<MASTER_OUTLINE .*HREF="([^"]*)"/i) {
  138.    if ($master_outline_URL_set != 0)
  139.      {die "Cannot redefine master lesson outline URL"};
  140.    $master_outline_URL = $1;
  141.    $master_outline_URL_set = 1;
  142.    print "Master outline URL is $master_outline_URL\n";
  143.    }
  144.   elsif (m/^ *<TUTOR_HOME_PAGE .*HREF="([^"]*)"/i) {
  145.    if ($tutor_home_page_set != 0) {die "Cannot redefine tutor home page URL"};
  146.    $tutor_home_page_URL = $1;
  147.    $tutor_home_page_URL_set = 1;
  148.    print "Tutor home page URL is $tutor_home_page_URL\n";
  149.    }
  150.   elsif (m/^<SECTION /i) {last}
  151.   elsif (m/^ *$/) {} # Do nothing with blank lines.
  152.   else {
  153.     print "WARNING: LINE IN HEADER OF FILE IGNORED, TEXT IS:";
  154.     print $_;
  155.   }
  156.  }
  157.  # We found a SECTION header or end of file. Stop processing lesson header.
  158. }
  159.  
  160. sub process_input_body {
  161.   # Process body of a lesson file (it should have 2 or more sections).
  162.  
  163.   # Errorcheck - we should have gotten TUTOR, LESSON, and AUTHOR.
  164.  
  165.   if ($lesson_number < 1)
  166.      {die "Sorry, no valid lesson number encountered!"};
  167.  
  168.   # Set up constants used in the rest of the program & open outline file.
  169.   # This is the filename for the generated outline.
  170.   $lesson_outline = "lesson${lesson_number}" . $html_extension;
  171.   if (!open(OUTLINE, ">${lesson_outline}.new"))
  172.     {die "Sorry, cannot open output lesson outline file ${lesson_outline}"};
  173.   &substitute_and_append("outline.head", "outline");
  174.  
  175.   # Process body of .les file.
  176.  
  177.   while ($_ ne "") {  # While there's still some text to process.
  178.     if (m/^<SECTION /i) { &process_section; }
  179.     else { print "Warning: text ignored outside of a section: $_" };
  180.     &read_line; # Done processing; read in the next line.
  181.   }
  182.  
  183.   # Done with all sections; close everything up.
  184.   &close_section_if_necessary;
  185.   &substitute_and_append("outline.tail", "outline");
  186.   close(OUTLINE);
  187.   close(IN);
  188. }
  189.  
  190. sub process_section {
  191.  # Process a single section from a lesson file.
  192.  # Precondition: current line should have a SECTION command.
  193.  &close_section_if_necessary; # Close previous section if necessary.
  194.  $section_number++;
  195.  # In the last section, use "f" as the section number in the filename
  196.  # so that the next lesson can link back to it correctly.
  197.  if ($section_number == $number_of_sections)
  198.     {$section_number_file_rep = "f"}
  199.  else
  200.     {$section_number_file_rep = $section_number};
  201.  # The only way to get here is if $_ contains <SECTION ...>; get its name.
  202.  $title= $_;
  203.  $title =~ s/^<SECTION .*NAME="([^"]*)".*$/$1/i;
  204.  chop($title);
  205.  print "Processing Section ${lesson_number}.${section_number}, ";
  206.  print "Name (Title) = $title\n";
  207.  $section_filename="s${lesson_number}-${section_number_file_rep}" .
  208.                    $html_extension;
  209.  if (!open(SECTION, ">${section_filename}.new"))
  210.    {die "Sorry, cannot open section file ${section_filename}"};
  211.  $section_file_open = 1;
  212.  
  213.  &determine_next_section_URL;
  214.  &determine_previous_section_URL;
  215.  $section_question_URL = "s${lesson_number}-" .
  216.                          "${section_number_file_rep}" . $html_extension .
  217.                          "#quiz";
  218.  $section_has_question = 0; # We haven't seen section question yet.
  219.  
  220.  print OUTLINE '<LI><A HREF="', ${section_filename}, '">', ${title}, "</A>\n";
  221.  
  222.  &reset_set_response;
  223.  # Insert the section header.
  224.  &substitute_and_append("section.head", "section");
  225.  
  226.  while (!eof(IN)) {
  227.   &read_line;
  228.  
  229.   # Is this the start of a question?
  230.   if (m/^<QUESTION[ >]/i) {&process_question;};
  231.  
  232.   # Is this the end of the section?
  233.   if (m/^<SECTION /i) {&process_section; last}; # recurse.
  234.   if (m/^<\/SECTION /i) {&close_section_if_necessary; last};
  235.  
  236.   # Normal text, just append it into the section.
  237.   print SECTION $_;
  238.  }
  239. }
  240.  
  241.  
  242. sub close_section_if_necessary {
  243.  if ($section_file_open == 1) {
  244.    if ($section_has_question == 0) {
  245.       # No quiz question - insert the no_question template.
  246.       &substitute_and_append("section.no_question", "section");
  247.    }
  248.    &substitute_and_append("section.tail", "section");
  249.    close(SECTION);
  250.    $section_file_open = 0;
  251.  }
  252. }
  253.  
  254. sub substitute_and_append {
  255.  local($template_chunk, $file_to_append) = @_;
  256.  
  257.  # Append to $file_to_append the contents of $template_chunk
  258.  # after performing appropriate substitutions.
  259.  
  260.  # $template_chunk is a string containing the name of the template chunk
  261.  #              from the template file to read in & perform substitutions on.
  262.  # $file_to_append is a string containing the name of the file handler
  263.  #              to append to. This is a kludge; ideally we would pass in
  264.  #              actual file handle, but perl apparantly can't pass file
  265.  #              handles, so we pass in a string instead.
  266.  
  267.  local($template_text) = $template{ $template_chunk };
  268.  
  269.  # Substitute markers in the template with their current values.
  270.  
  271.  $template_text =~ s/\[TUTOR_NAME\]/${tutor_name}/g;
  272.  $template_text =~
  273.      s/\[FULL_SECTION_NUMBER\]/${lesson_number}.${section_number}/g;
  274.  $template_text =~ s/\[SECTION_TITLE\]/${title}/g;
  275.  $template_text =~ s/\[SECTION_NAME\]/${title}/g; # likely misspelling
  276.  $template_text =~ s/\[AUTHOR_EMAIL\]/${author_email}/g;
  277.  $template_text =~ s/\[AUTHOR_ADDRESS\]/${author_address}/g;
  278.  $template_text =~ s/\[AUTHOR_NAME\]/${author_name}/g;
  279.  $template_text =~ s/\[ANSWER_NUMBER\]/${current_response}/g;
  280.  $template_text =~ s/\[LESSON_NUMBER\]/${lesson_number}/g;
  281.  $template_text =~ s/\[LESSON_OUTLINE_URL\]/${lesson_outline_URL}/g;
  282.  $template_text =~ s/\[TUTOR_HOME_PAGE_URL\]/${tutor_home_page_URL}/g;
  283.  
  284.  $template_text =~ s/\[MASTER_OUTLINE_URL\]/${master_outline_URL}/g;
  285.  $template_text =~ s/\[TODAYS_DATE\]/${todays_date}/g;
  286.  $template_text =~ s/\[TODAYS_YEAR\]/${todays_year}/g;
  287.  
  288.  # These values may change from section to section.
  289.  $template_text =~ s/\[NEXT_SECTION_URL\]/${next_section_URL}/g;
  290.  $template_text =~ s/\[PREVIOUS_SECTION_URL\]/${previous_section_URL}/g;
  291.  $template_text =~
  292.    s/\[THE_PREVIOUS_SECTION_OR_HOME\]/${the_previous_section_or_home}/g;
  293.  $template_text =~
  294.    s/\[CONFIRM_SKIP_SECTION_URL\]/${confirm_skip_section_URL}/g;
  295.  $template_text =~
  296.    s/\[SECTION_QUESTION_URL\]/${section_question_URL}/g;
  297.  
  298.  
  299.  # Determine where to append this line & append it.
  300.  if    ($file_to_append eq "section")  {print SECTION  $template_text;}
  301.  elsif ($file_to_append eq "response") {print RESPONSE $template_text;}
  302.  elsif ($file_to_append eq "outline")  {print OUTLINE  $template_text;}
  303.  else {die "Unexpected append parameter value $file_to_append!"};
  304. }
  305.  
  306. sub process_question {
  307. # Process the question in the section.
  308.  
  309.  $section_has_question = 1;
  310.  &substitute_and_append("section.question.head", "section");
  311.  
  312.  # Eliminate marker. If there's text left on this line, use it.
  313.  s/<QUESTION[^>]*> *//i;
  314.  chop;
  315.  if ($_ ne "") {print SECTION "$_\n"};
  316.  
  317.  # Copy text until there's a <CHOICES> marker.
  318.  while (!eof(IN)) {
  319.   &read_line;
  320.   if (/^<CHOICES/) {last};
  321.   if (/^<SECTION/) {die "Error - previous question had no choices."};
  322.   print SECTION;
  323.  }
  324.  
  325.  # We've sent out the question to its end.
  326.  
  327.  &process_choices;
  328.  &process_optional_responses;
  329.  &generate_default_responses;
  330. }
  331.  
  332.  
  333. sub read_line {
  334. # Read line from file handle <IN>. Sets $_ to its new value, and returns it.
  335. # Discards <COMMENT > lines.
  336.  $_ = $input_line = <IN>;
  337.  if (/^ *<COMMENT /i) {&read_line}; # Don't include COMMENT lines.
  338.  if (/^ *<TEXT /i) {&read_textfile}
  339.  else {$input_line;} # Return the value of the line read in.
  340. }
  341.  
  342. sub read_textfile {
  343. # Read in an entire non-HTML text file.
  344. # Sets $_ to the file contents, and returns $_'s value.
  345. # Performs substitutions so that "special" HTML characters
  346. # come out correctly (&, <, etc.)
  347.   m/^ *<TEXT .*FILE="([^"]*)"/i;
  348.   $text_filename=$1;
  349.   if (m/FONT=PRE/i) {$use_pre=1}
  350.   else              {$use_pre=0};
  351.   $file_contents = "";
  352.   if ($use_pre) {$file_contents .= "<PRE>\n"};
  353.   print "  Incorporating text (non-HTML) file ${text_filename}.\n";
  354.   if (!open(TEXTFILE, "<${text_filename}"))
  355.     {die "Sorry, cannot open text file ${text_filename}"};
  356.   while (<TEXTFILE>) {
  357.     # Substitute special HTML characters.
  358.     s/\&/&/g;   # & becomes &
  359.     s/\</</g;    # < becomes <
  360.     s/\>/>/g;    # > becomes >
  361.     # Do _NOT_ substitute the double-quote character (" into ")
  362.     # Windows NCSA Mosaic version 2.0 alpha 2 doesn't handle it,
  363.     # and all the browsers I can find can display them quite well.
  364.     #  s/\"/"/g;  # " becomes "
  365.     $file_contents .= $_;  # Add the contents.
  366.   }
  367.   close(TEXTFILE);
  368.   if ($use_pre) {$file_contents .= "</PRE>\n"};
  369.   $_ = $file_contents;  # Set the value, and return it.
  370. }
  371.  
  372. sub process_choices {
  373.  # Process the choices portion of a question in a section.
  374.  
  375.  # It must be of the form:
  376.  # <CHOICES>
  377.  # <CHOICE ANS=1>Text_IO
  378.  # </CHOICES>
  379.  # <ANSWER ANS=2>
  380.  # Note: the numbers in the <CHOICE> item are ignored.
  381.  
  382.  die "CHOICES section expected." unless /^<CHOICES/;
  383.  
  384.  print SECTION "<OL>\n";
  385.  $choice_number = 0;
  386.  while (!eof(IN)) {
  387.    &read_line;
  388.    if (/^<\/CHOICES/) {last};
  389.    if (/^<CHOICE/) {
  390.      $choice_number++;
  391.      s/^<CHOICE[^>]*> *//i;
  392.      chop;
  393.      print SECTION '<LI><A HREF="' .
  394.                    "s${lesson_number}-${section_number_file_rep}" .
  395.                    "r${choice_number}${html_extension}\">";
  396.      print SECTION $_;
  397.      print SECTION "</A>\n";
  398.      }
  399.    else {die "Sorry, choice text must be on the same line as <CHOICE>."}
  400.  }
  401.  $number_of_choices = $choice_number;
  402.  
  403.  die "A Choice section must end with </CHOICES>" unless (/^<\/CHOICES>/i);
  404.  &read_line;
  405.  
  406.  if (/^<ANSWER .*ANS=([0-9])+>/i) {
  407.    $correct_answer = $1 + 0;
  408.  } else {
  409.    die "A Choice section must be followed with the answer, " .
  410.        "format: <ANSWER ANS=number>";
  411.  }
  412.  &read_line;  # Get the next line for the callee.
  413.  
  414.  &substitute_and_append("section.question.tail", "section");
  415. }
  416.  
  417. sub process_optional_responses {
  418.  # Process the optional response portion of a section:
  419.  # <RESPONSES>
  420.  # <WHEN ANS=1> ..text..
  421.  # </RESPONSES>
  422.  # A <SECTION> could also follow.
  423.  
  424.  # Skip blank lines, if any.
  425.  while ($_ || !eof(IN)) {
  426.    if (/<RESPONSES>/i) {last};
  427.    if (/<SECTION/i) {last};
  428.    chop;
  429.    if ($_ ne "") {print "Warning: line ignored: $_\n"};
  430.    &read_line;
  431.  }
  432.  
  433.  if (/<RESPONSES>/i) {
  434.    # We have a response section.
  435.    $current_response = 0;
  436.    while (&read_line) {
  437.      if (/<\/RESPONSES>/i) {&read_line; last};
  438.      if (/<WHEN .*>/i) {
  439.        &close_response_if_necessary;
  440.        if (/<WHEN ANS=[0-9]*>/i) {
  441.          $current_response = $_;
  442.          $current_response =~ s/^<WHEN *ANS=([0-9]+)>.*$/$1/i;
  443.          chop($current_response);
  444.        }
  445.        else {
  446.          if (/<WHEN CORRECT>/i) {$current_response = $correct_answer}
  447.          else {die "Invalid WHEN clause"}
  448.         };
  449.        $set_response[$current_response] = 1; # Mark this response as found
  450.        &open_response;
  451.  
  452.        # If there's text on the same line as the WHEN clause, put it
  453.        # in the response file.
  454.        s/^ *<WHEN[^>]*> *//i;
  455.        chop;
  456.        if ($_ ne "") {print RESPONSE "$_\n"};
  457.      } else {
  458.         # We got some text, presumably part of a response.
  459.         if ($current_response == 0) {
  460.            print "Warning: Responses not associated with a specific answer\n";
  461.            print "Text is: $_";
  462.         } else {
  463.            print RESPONSE;
  464.         }
  465.      }
  466.    }
  467.    &close_response_if_necessary;
  468.  }
  469. }
  470.  
  471. sub open_response {
  472.  # Open the response file & set the variable names referring to it.
  473.  $response_filename="s${lesson_number}-${section_number_file_rep}" .
  474.                     "r${current_response}${html_extension}";
  475.  if (!open(RESPONSE, ">${response_filename}.new"))
  476.       {die "Sorry, cannot open response file ${response_filename}.new"};
  477.  print "  Opened response file ${response_filename}\n";
  478.  $response_file_open = 1;
  479.  &substitute_and_append("response.head", "response");
  480. }
  481.  
  482. sub close_response_if_necessary {
  483.  if ($response_file_open == 1) {
  484.    if ($current_response == $correct_answer)
  485.     {&substitute_and_append("response.correct.tail", "response");}
  486.    else
  487.     {&substitute_and_append("response.incorrect.tail", "response");};
  488.    &substitute_and_append("response.tail", "response");
  489.    close(RESPONSE);
  490.    $response_file_open = 0;
  491.  }
  492. }
  493.  
  494. sub generate_default_responses {
  495.  $current_response = 1;
  496.  while ($current_response <= $number_of_choices) {
  497.    if ($set_response[$current_response] == 0) {
  498.       &open_response;
  499.       # insert default text.
  500.       if ($current_response == $correct_answer) {
  501.          &substitute_and_append("response.correct.default", "response");
  502.       } else {
  503.          &substitute_and_append("response.incorrect.default", "response");
  504.       }
  505.       &close_response_if_necessary;
  506.    }
  507.    $current_response++;
  508.  }
  509. }
  510.  
  511. sub determine_next_section_URL {
  512. # Set $next_section_URL, $confirm_skip_to_next_section_URL
  513.  if ($section_number == $number_of_sections) {
  514.     $next_prefix = $next_lesson_location;
  515.     $next_lesson_number = $lesson_number + 1;
  516.     $next_section_file_rep = "1";
  517.  }
  518.  else {
  519.     $next_prefix = "";
  520.     $next_lesson_number = $lesson_number;
  521.     $next_section_file_rep = $section_number + 1;
  522.     if ($next_section_file_rep == $number_of_sections)
  523.       {$next_section_file_rep = "f"};
  524.    }
  525.   $next_section_URL = "${next_prefix}s${next_lesson_number}" .
  526.                      "-${next_section_file_rep}${html_extension}";
  527.  
  528.  # Set $confirm_skip_to_next_section_URL, the URL to go to if
  529.  # the user selects "skip" from a section.
  530.  
  531.  if ($lesson_number == 1 && $section_number == 1) {
  532.    # For lesson 1, section 1, confirm the skipping of the quiz
  533.    # by linking to the special file "skip1-1.html".
  534.    $confirm_skip_section_URL = 'skip1-1' . $html_extension;
  535.  } else {
  536.    $confirm_skip_section_URL = $next_section_URL;
  537.  };
  538. }
  539.  
  540. sub determine_previous_section_URL {
  541.  # Determine & set the value of:
  542.  # $previous_section_URL
  543.  # $the_previous_section_or_home
  544.  
  545.  $the_previous_section_or_home = $template{"the_previous_section_is_not_home"};
  546.  if ( $the_previous_section_or_home == "" ) 
  547.     { $the_previous_section_or_home = "the previous section" };
  548.  local($template_text) = $template{ $template_chunk };
  549.  if ($section_number == 1) {
  550.      $previous_lesson_number = $lesson_number - 1;
  551.      if ($lesson_number == 1) {
  552.         # lesson 1, section 1 - the "previous" section is the home page.
  553.         $previous_section_URL= $tutor_home_page_URL;
  554.         $the_previous_section_or_home =
  555.                     $template{"the_previous_section_is_home"};
  556.         if ( $the_previous_section_or_home == "" ) 
  557.              { $the_previous_section_or_home = "the tutorial home page" };
  558.      } elsif ($previous_lesson_location_set == 1) {
  559.         $previous_section_URL= $previous_lesson_location . '/' .
  560.                       's' . $previous_lesson_number . '-f' . $html_extension;
  561.      } else {
  562.         # No previous location set; must be current directory.
  563.         $previous_section_URL= 's' . $previous_lesson_number . '-f' .
  564.                                $html_extension;
  565.      }
  566.  } else {
  567.      $previous_lesson_number = $lesson_number;
  568.      $previous_section_number = $section_number - 1;
  569.      $previous_section_URL= 's' . $previous_lesson_number . '-' .
  570.                             $previous_section_number . $html_extension;
  571.  }
  572.  
  573. }
  574.  
  575.  
  576. sub move_in_new_files {
  577. # Move in all files with .new extensions to replace original files.
  578. # If desired, this can be modified so it only replaces "changed" files.
  579.  print "\n";
  580.  print "Replacing changed files, if any.\n";
  581.  while (<*.new>) {
  582.    $oldname = $_;
  583.    $newname = $_;
  584.    $newname =~ s/\.new$//;
  585.    if (system("cmp -s $oldname $newname") != 0) {
  586.      # The new file is different, remove the older one.
  587.      print " Replacing $newname (with $oldname)\n";
  588.      rename($newname, "${newname}.BAK"); # Keep older one, just in case.
  589.      rename($oldname, $newname);
  590.    } else {
  591.      unlink($oldname);
  592.    }
  593.  }
  594. }
  595.  
  596. sub change_timestamp_file {
  597. # Change the timestamp file. Parameter 1 is the input filename.
  598. # The timestamp is used so we'll know when this file was last generated.
  599. local($infile_name) = @_;
  600. $timestamp_filename = $infile_name;
  601. $timestamp_filename =~ s/\.les$//;
  602. $timestamp_filename .= ".tim";
  603. if (!open(TIMESTAMP_FILE, ">${timestamp_filename}"))
  604.    {die "Sorry, cannot create timestamp file ${timestamp_filename}"};
  605. print TIMESTAMP_FILE "Lesson Generated.\n";
  606. close(TIMESTAMP_FILE);
  607. }
  608.  
  609. sub read_in_template {
  610. # Read in file "template", putting results in associative array %template
  611. local($current_template_unit);
  612. $current_template_unit = "garbage";
  613. if (!open(TEMPLATE, "<template"))
  614.    {die "Sorry, cannot find a local file named template."};
  615. while (<TEMPLATE>) {
  616.   if (/^===== (\S+) =====/)
  617.     {$current_template_unit = $1;}
  618.   elsif ($current_template_unit eq "comment")
  619.      {} # Do nothing with comments.
  620.   elsif ($current_template_unit eq "garbage")
  621.     {print "WARNING: Garbage line in file template: $_";}
  622.   else { $template{$current_template_unit} .= $_; } # Append to assoc array.
  623. };
  624. close(TEMPLATE);
  625.  
  626. # If debug on, print out the %template contents.
  627. if ($debug_template == 1) {
  628.   print "DEBUG: Printing out template contents as read:\n";
  629.   foreach $i (%template) {
  630.    print "=====\n";
  631.    print $i;
  632.    print "\n";
  633.   }
  634. }
  635. }
  636.  
  637. sub reset_set_response {
  638.  # Reset array "set_response", which stores the list of responses
  639.  # set in this section by the user. This is used to determine
  640.  # which responses should have "default" responses set for them.
  641.  
  642.  # Assume no more than 9 responses.
  643.  @set_response = ( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
  644.  $choice_number = 0;
  645. }
  646.  
  647. sub initialize_variables {
  648. # Note - these are GLOBAL variables.
  649.  $lesson_number = 0;
  650.  $section_number = 0;
  651.  $tutor_name="";
  652.  $author_name="";
  653.  $author_email="";
  654.  $author_address="";
  655.  $section_has_question = 0;  # Does this section have a question? 0=false.
  656.  
  657.  $previous_lesson_location_set = 0;
  658.  $previous_lesson_location ="";
  659.  
  660.  $next_lesson_location_set = 0;
  661.  $next_lesson_location ="";
  662.  
  663.  $master_outline_set = 0;
  664.  $master_outline_URL ="";
  665.  
  666.  $tutor_home_page_set = 0;
  667.  $tutor_home_page_URL = "";
  668.  
  669.  $section_file_open = 0; # 0=false, 1=true.
  670.  $response_file_open = 0;
  671.  $current_response = 0;
  672.  
  673.  # Determine today's date and year.
  674.  ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
  675.                                                           localtime(time);
  676.  $mon++; # Month was returned as 0-11, so we need to fix it.
  677.  # Determine 4-digit year; recode in 2090.
  678.  if ($year < 90) { $year += 2000 } else { $year += 1900 };
  679.  $todays_date =  $year . "." . $mon . "." . $mday . " (YY.MM.DD)";
  680.  $todays_year = $year;
  681.  print "Today's date is ${todays_date}; year is $todays_year\n";
  682. }
  683.  
  684. sub open_lesson_file {
  685.  # Get lesson file name and find out how many sections it has.
  686.  if (!open(IN, "<$lesson_source"))
  687.    {die "Sorry, cannot open input file ${lesson_source}"};
  688.  $number_of_sections=0;
  689.  while (<IN>) {
  690.   if (m/^<SECTION /i) {$number_of_sections++};
  691.   };
  692.  close(IN);
  693.  print "There are $number_of_sections sections in " .
  694.        "lesson file ${lesson_source}.\n";
  695.  if (!open(IN, "<$lesson_source"))
  696.     {die "Cannot re-open input file ${lesson_source}"};
  697. }
  698.  
  699. sub read_in_defaults {
  700.  # If it exists, load in the file "defaults" as a lesson header file.
  701.  # This makes it easier to create 'standard defaults' for a specific
  702.  # tutorial.
  703.  if (open(IN, "<default")) {
  704.    print "Default file found; processing it first.\n";
  705.    &process_input_header;
  706.    close(IN);
  707.  } else {
  708.    print "(No default file found.)\n";
  709.  }
  710. }
  711.  
  712.  
  713. # Potential projects:
  714. # ?? Process <INCLUDE >
  715. # ?? Clean up code (use perl shortcuts, examine for EOF).
  716. # ?? Error check input. For head, warn of unused.
  717. # ?? More error checking in body processing.
  718.