################################################################################
# WeBWorK
# 
# Copyright (c) 1995-2001 WeBWorK Team, University of Rochester
# All rights reserved
# 
# $Id$
################################################################################

###################################
## Begin Global
###################################

package Global;

# The variables defined in this package set defaults and parameters for
# the whole weBWorK system.  Defaults can be over ridden for individual
# courses by redefining variables in the individual course
# webworkCourse.ph file. For example the default SYSTEM feedback address
# set below as: $feedbackAddress = 'webwork@math.rochester.edu'; can (and
# should) be over ridden for an individual course by entering e.g.
# $Global::feedbackAdress  = 'apizer@math.rochester.edu,
# gage@math.rochester.edu'; in the individual course webworkCourse.ph
# file. Of course you should really enter the email address(es) of the
# professors teaching the course.

# The system_webwork_setup.pl script also creates the file webworkConfig.pm
# in the lib directory. This file contains a cgiDebugMode parameter than can
# be set to aid in the debugging of WeBWorK scripts.

################################
# Local configuration settings #
################################

# $legalAddress defines destination addresses which are accepted for use in
# scripts that send mail. it is a perl regular expression.
$legalAddress = '^[^@]+@([-\w]+\.)+[A-Za-z]{2,4}$';
#$legalAddress = '^[^@]+@([-\w]+\.)+rochester.edu$';

# These define the default addresses to which will be used by the system.
$webmaster       = 'webwork@math.rochester.edu';
$feedbackAddress = $webmaster; # should be redefined for each course in webworkCourse.ph
$defaultFrom     = $webmaster; # should be redefined for each course in webworkCourse.ph
$defaultReply    = $webmaster; # should be redefined for each course in webworkCourse.ph
$smtpSender	     = $webmaster; # should be redefined for each course in webworkCourse.ph

# $smtpServer is the address of the sendmail server. If you are running sendmail on the
# same machine as webwork, use "localhost"
$smtpServer = 'mail.math.rochester.edu';

# $dirDelim is the delimiter used in pathnames on your system.
$dirDelim = '/';

###########################
# Local external programs #
###########################

# tth is used by the formatted-text display mode.
$externalTTHPath = "/usr/local/bin/tth";

# latex2html is used my the typeset display mode. WeBWorK supports version 96.1
# and version 98.1p1 (or later). Specify either 96.1 or 98.1p1 for
# $externalLaTeX2HTMLVersion -- this will effect the syntax used when calling
# latex2html. $externalLaTeX2HTMLInit should point to a latex2html init file
# that matches the version of latex2html specified.
$externalLaTeX2HTMLPath = "/usr/local/bin/latex2html";
$externalLaTeX2HTMLVersion = "98.1p1";
$externalLaTeX2HTMLInit = "${mainDirectory}lib/latex2html.init.$externalLaTeX2HTMLVersion";

# LaTeX2HTML calls TeX utilities without using their full pathnames. Thus,
# if the path to the binaries is not in the environment under which CGI
# scripts are called (usually /bin:/usr/bin), you will have to specify it
# here, so that it can be added before calling LaTeX2HTML. This path
# should contain the 'latex' binary.
$externalLaTeX2HTMLSupportPath = "/usr/local/bin";

# latex, dvips, and gs are programs used to generate problem set hard copy
# output in various formats (DVI, PostScript, PDF).
$externalLatexPath = "/usr/local/bin/latex";
$externalDvipsPath = "/usr/local/bin/dvips";
$externalGsPath = "/usr/local/bin/gs";

#####################
# Internal settings #
#####################

# The access log is stored in the logs/ directory under the system directory
# in a file called "access_log". It contains information about virtually
# every action committed by users, including all answers submitted.
# Usually this information is unneccessary, and the file becomes
# large very quickly, so this log is ordinarily turned off. However, the
# information it contains might be useful if, for example, a student wants an
# extension and claims not to have viewed the correct answers. Set to 1 to
# enable the access log; set to 0 to disable.
$logAccessData = 0;

# the number of digits in each PSVN controls how many unique PSVNs can be
# created. For a PSVN of n digits, the range of available PSVNs will be
# [10^(n-1),10^n-1]. The number of available PSVNs must be more than
# number_of_students*number_of_problem_sets for any given course.
$psvn_digits = 5;

# The default rendering mode for onscreen problem display:
# 'HTML'       = raw TeX source
# 'HTML_tth'   = HTML formatted with TTH
# 'Latex2HTML' = Images generated by LaTeX2HTML
$htmlModeDefault = 'HTML';



## Change DBtie_file only if you want to change the default database. The script
## db_tie.pl uses DB_File (the Berkeley DB) and  gdbm_tie.pl uses GDBM_File.  This
## setting can be changed for an individual course in the webworkCourse.ph file. For
## some other database, you will have to write your own database tie-file. Such
## files reside in the scripts directory.
#$DBtie_file = 'db_tie.pl';
$DBtie_file = 'gdbm_tie.pl';

####################################################################################
########### There should be no need to customize after this point  #################
####################################################################################

use sigtrap;
use diagnostics;
use webworkConfig;
use PGtranslator;
		# this is so that PGtranslator->evalute_macros is available when webworkCourse.ph is processed.

require 5.000;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(
	getWebworkScriptDirectory
	getWebworkCgiURL
	getCourseMOTDFile
	getSystemMOTDFile	 
	getCourseDatabaseDirectory
	getCourseDatabaseTieFile
	getCourseLogsDirectory
	getCourseTemplateDirectory
	getCourseURL
	getCourseScoringDirectory
	getCourseScriptsDirectory
	getCourseMacroDirectory
	getCourseHtmlDirectory
	getCourseEmailDirectory
	getCourseTempDirectory
	getCourseTempURL
	getCourseClasslistFile
	getCourseEnvironment
	getCourseKeyFile
	getCoursePasswordFile
	getCoursePermissionsFile
	getCourseDatabaseFile
	getCourseHtmlURL
	getCoursel2hDirectory
	getCoursel2hURL
	getDirDelim
	getDelim
	getScoreFilePrefix
	getScoring_log
	getDash
	getDat
	getBbext
	getStatusDrop
	convertPath
	getNumRelPercentTolDefault
	getNumZeroLevelDefault
	getNumZeroLevelTolDefault
	getNumAbsTolDefault
	getNumFormatDefault
	getFunctRelPercentTolDefault
	getFunctZeroLevelDefault
	getFunctZeroLevelTolDefault
	getFunctAbsTolDefault
	getFunctNumOfPoints
	getFunctVarDefault
	getFunctLLimitDefault
	getFunctULimitDefault
	getFunctMaxConstantOfIntegration
	getLoginURL
	getWebworkLogsDirectory
	wwerror
	getAllowDestroyRebuildProbSets
);

## URL's derived from webworkConfig.pm
$loginURL		=	"${cgiWebworkURL}login.pl";
$imagesURL		=	"${htmlWebworkURL}images/";
$helpURL		=	"${htmlWebworkURL}helpFiles/";
$webworkDocsURL	=	'http://webwork.math.rochester.edu/docs/docs/';
$appletsURL		=	"${htmlWebworkURL}applets/";

## practice users
$practiceUser	=	'practice';  # name of password-less "practice" user
$practiceKey	=	'practice';  # a dummy key for this user, can be anything


## The database handle for using mSQL:
$dbh = 0;

## various gifs
$helpGifUrl			=	"${imagesURL}ww_help.gif";
$logoutGifUrl		=	"${imagesURL}ww_logout.gif";
$feedbackGifUrl		=	"${imagesURL}ww_feedback.gif";
$currentImgUrl		=	"${imagesURL}ww_curr.gif";
$previousImgUrl		=	"${imagesURL}ww_prev.gif";
$nextImgUrl			=	"${imagesURL}ww_next.gif";
$problistImgUrl		=	"${imagesURL}ww_problist.gif";
$upImgUrl			=	"${imagesURL}ww_up.gif";
$bluesquareImgUrl	=	"${imagesURL}ww_bluesq.gif";
$headerImgUrl		=	"${imagesURL}webwork.gif";			# image to include at top of pages
$squareWebworkGif	=	"${imagesURL}square_webwork.gif";	# image to include at top of pages

## backgrounds gifs for HTML documents
$background_plain_url	=	"${imagesURL}white.gif";
$background_okay_url	=	"${imagesURL}green.gif";
$background_warn_url	=	"${imagesURL}red.gif";
$bg_color				=	'#EFEFEF';   #background color for processProblem

## Directories
$coursesDirectory		=	convertPath("${mainDirectory}courses/");
$scriptDirectory		=	convertPath("${mainDirectory}scripts/");
$cgiDirectory			=	convertPath("${mainDirectory}cgi/");
#$tempDirectory			=	convertPath("/tmp/");
$authDirectory			=	convertPath(".auth/");
$courseScriptsDirectory	=	convertPath("${mainDirectory}courseScripts/");
$macroDirectory			=	convertPath("${mainDirectory}courseScripts/");
$classDirectory			=	'';  #This must be defined in webworkCourse.ph
$webworkLogsDirectory	=	"${mainDirectory}/logs/";

## File names
$coursesFilename		=	'courses-list';
$coursesFile			=	"${coursesDirectory}$coursesFilename";
$classlistFilename		=	'classlist.lst';
$keyFilename			=	'keys';
$passwordFilename		=	'password';
$permissionsFilename	=	'permissions';
$database				=	'webwork-database';
$CL_Database       		= 	'classlist-database';
$tipsFilename			=	'tips.txt';
$system_motd_filename	=	'motd.txt';
$course_motd_filename	=	'motd.txt';
$tipsFile				=	"${mainDirectory}$tipsFilename";

#  CGI script calls

$login_CGI			=	"${cgiWebworkURL}login.pl";
$logout_CGI			=	"${cgiWebworkURL}logout.pl";
$welcome_CGI		=	"${cgiWebworkURL}welcome.pl";
$welcomeAction_CGI	=	"${cgiWebworkURL}welcomeAction.pl";
$processProblem_CGI	=	"${cgiWebworkURL}processProblem8.pl";
$feedback_CGI		=	"${cgiWebworkURL}feedback.pl";
$problemEditor_CGI	=	"${cgiWebworkURL}problemEditor.pl";

##  The following items control how the whole problem set is typeset by downloadPS.pl.
##  The files (e.g. "tex_set_ptramble.tex") are found in the .../templates directory
##  Note that the system dependent latex and dvips programs are defined in the file
##  makePS which is in the .../scripts directory.  makePS must be edited to call
##  the correct programs.
##

##  This is the tex preamble file used by downloadPS.pl in typeseting the whole problem set.
##  E.g. loads AMS latex and graphics packages, some macro definitions.
$TEX_SET_PREAMBLE = 'texSetPreamble.tex';

##  This is the tex header file used by downloadPS.pl in typeseting the whole problem set
##  E.g. loads two column format, macro definitions.
$TEX_SET_HEADER = '';

##  This is the header file used by downloadPS.pl to enter preliminary verbiage for the
##  whole problem set. E.g. Course name, student name, problem set number,instructions, due date.
$SET_HEADER = 'paperSetHeader.pg';

##  This is the tex footer file used by downloadPS.pl in typeseting the whole problem set
$TEX_SET_FOOTER = 'texSetFooter.tex';


##  The following items control how an individual problem is typeset by processProblem.pl
##  and l2h.  Note that the system dependent latex2html program is defined in processProblem.pl.  It
##  should really be in a seperate file like makeps.

##  This is the tex preamble file used by processProblem.pl typeseting an individual problem
##  Usually very similar to $TEX_SET_PREAMBLE
$TEX_PROB_PREAMBLE = 'texProbPreamble.tex';

##  This is the tex header file used by processProblem.pl typeseting an individual problem
$TEX_PROB_HEADER = '';


##  This is the header file used by probSet.pl to enter preliminary verbiage on the prob set
##  page. E.g. Instructions, due date.
$PROB_HEADER = 'screenSetHeader.pg';

##  This is the tex footer file processProblem.pl typeseting an individual problem
$TEX_PROB_FOOTER = 'texProbFooter.tex';



$courseEnvironmentFile	=	'webworkCourse.ph';
#$webworkCourse_ph		=	'webworkCourse.ph';
$DBglue_pl				=	'DBglue8.pl';
$HTMLglue_pl			=	'HTMLglue.pl';
$classlist_DBglue_pl 	=	'classlist_DBglue.pl';
$FILE_pl				=	'FILE.pl';
$displayMacros_pl		=	'displayMacros.pl';
$scoring_log			=	'scoring.log';
#####$probSetHeader		=	'probSetHeader';
$SCRtools_pl			=	'pScSet6.pl';
$buildProbSetTools		=	'proceduresForBuildProbSetDB.pl';


## File and Directory permissions

## e.g. S1-1521.sco in (base course directory)/DATA
$sco_files_permission = 0660;

## tie permissions (used in tie commands)
## The database, password, and permissions files
## always take their permissions from the Global
## vaiables below.  The important keys file
## takes its permission from $restricted_tie_permission.
$restricted_tie_permission = 0600;
$standard_tie_permission = 0660;

## webwork-database in (base course directory)/DATA
$webwork_database_permission = 0660;

## password in (base course directory)/DATA/.auth
$password_permission = 0660;

## permissions in (base course directory)/DATA/.auth
$permissions_permission = 0660;

## e.g. s1ful.csv in (base course directory)/scoring
$scoring_files_permission = 0660;

## e.g. s1bak1.csv in (base course directory)/scoring
$scoring_bak_files_permission = 0440;

## e.g. 8587l2h.log  in (base course directory)/html/tmp/l2h
$l2h_logs_permission = 0660;

## e.g. set1/ in (base course directory)/html/tmp/l2h
$l2h_set_directory_permission = 0770;

## e.g. 1-1082/ in (base course directory)/html/tmp/l2h/set1
$l2h_prob_directory_permission  = 0770;

## e.g. 1082output.html in (base course directory)/html/tmp/l2h/set1/1-1082
$l2h_data_permission = 0770;

## e.g. file.gif in (base course directory)/html/tmp/gif
$tmp_file_permission = 0660;

## e.g. gif/ in (base course directory)/html/tmp/
$tmp_directory_permission = 0770;

##e.g. classlist files (e.g. MTH140A.lst) in (base course directory)/templates/
$classlist_file_permission = 0660;

##   Prefixes, extensions, defaults, etc

$scoreFilePrefix		=	'S';
$dash					=	'-';	# Can not be the underscore character or an upper or
								# lowercase letter or a digit. Used in .sco file names
$delim					=	',';	# used in scoring, classlist
$dat					=	'csv';
@statusDrop				=	qw(drop d withdraw);
$courselist_delim		=	'::';	# used by login.pl to get course names / dirs
$instructor_permissions	=	10;
$TA_permissions			=	5;

$score_decimal_digits	=	1;		# Number of decimal digits in recorded scores. For example
									# if this is 1 then on a 1 point problem that allows partial
									# credit, possible scores are 0, .1, .2, ..., 1.

$maxAttemptsWarningLevel = 5;
                        # If the set definition file puts a limit on the number of
                        # times a problem may be attempted, processProblem.pl will
                        # give a warning message when <= $maxAttemptsWarningLevel
                        # attempts remain.

$noOfFieldsInClasslist = 9;
                        # The number of fields in the classlist file. This is used as
                        # a check to make sure each record in the classlist file has
                        # this number of fields

$allowStudentToChangeEMAddress = 1; # setting to 1 allows students to change their
                                    # own email address. Setting to 0 disallows this

$Global::PG_environment{showPartialCorrectAnswers} = 1;  ## Set to 0 or 1. If set to 1 in multipart
                                    # questions the student will be told which parts are
                                    # correct and which are incorrect.  Usually, this is
                                    # set explicitly in each individual problem.

$allowDestroyRebuildProbSets = 0;   # Set to 0 or 1. If set to 1 a professor can destroy and
                                    # rebuild problems sets in one operation. This is very
                                    # convenient and powerful, but also very dangerous.  Usually
                                    # this is not allowed in courses students are using.  It is
                                    # often set to 1 in a private course being used only for
                                    # developing problem sets.  To do this, reset this in the
                                    # private course's webworkCourse.ph file.

$Global::PG_environment{recordSubmittedAnswers} = 1;	# Set to 0 or 1. If set to 1, submitted answers will be
									# stored. For example in a multipart question, a student can
									# do several parts, then logout or go onto another question. 
									# When they view the problem again, the answer blanks will be 
									# filled in with their most recent answers. This also allows 
									# professors to see exactly what a student entered. This default
									# can be over ridden for an individual problem by setting 
									# recordSubmittedAnswers in the .pg file for the problem. 

$maxSizeRecordedAns	= 256;			# Student answers longer than this length in bytes will not
									# stored in the database.	
									
$hide_studentID_from_TAs = 0;  		# Set to 0 or 1. If set to 1, studentID's will be hidden
									# from TA's.  For example some Universities may use SS#'s for
									# student ID's and you may not want TA's to view these.														

## arguments for flock()

$shared_lock      = 1;
$exclusive_lock   = 2;
$nonblocking_lock = 4;
$unlock_lock      = 8;

# These values provide defaults for the various answer comparison macros found
# in PGanswermacros.pl.  They can be over ridden for individual courses by
# redefining the variables in the individual course webworkCourse.ph file.
# They can be over ridden for individual problems by explicitly passing the
# desired values to the answer comparison macro

# The following effect numerical answer comparison
$numRelPercentTolDefault	=	.1;
$numZeroLevelDefault		=	1E-14;
$numZeroLevelTolDefault		=	1E-12;
$numAbsTolDefault			=	.001;
$numFormatDefault			=	'';		## use perl's format in prfmt()
# The following effect function comparison
$functRelPercentTolDefault	=	.1;
$functZeroLevelDefault		=	1E-14;
$functZeroLevelTolDefault	=	1E-12;
$functAbsTolDefault			=	.001;
$functNumOfPoints			=	3;
$functVarDefault			=	'x';
$functLLimitDefault			=	.0000001;
$functULimitDefault			=	.9999999;
# The following effects function comparison upto constant for antidifferentiation
$functMaxConstantOfIntegration = 1E8;

## These values provide defaults for the window size in the problemEditor.pl script
$editor_window_rows		=	25;
$editor_window_columns	=	90;

## This is the maximum number problems sets that can be downloaded at one time
## by a professor.  Set this higher or lower depending on the speed of your server

$max_num_of_ps_downloads_allowed = 20;


# Subroutines for defining the directories and URLs
###### Public vars/routines - these are imported into your namespace, #######
###### so they can be called as they are.
                  #######

sub getWebworkScriptDirectory	{ convertPath($scriptDirectory )  };
sub getCourseDatabaseDirectory	{ convertPath($databaseDirectory )};
sub getCourseDatabaseTieFile	{ convertPath($DBtie_file )};
sub getCourseLogsDirectory		{ convertPath($logsDirectory )};
sub getCourseTemplateDirectory	{ convertPath($templateDirectory )};
sub getCourseEmailDirectory     { convertPath("${templateDirectory}email/")};
sub getCourseScoringDirectory	{ convertPath($scoringDirectory ) };
sub getCourseHtmlDirectory		{ convertPath($htmlDirectory )};
sub getCourseTempDirectory		{convertPath($courseTempDirectory)};
sub getCoursel2hDirectory		{ convertPath( "${courseTempDirectory}l2h/" )};
sub getCourseScriptsDirectory	{ convertPath($courseScriptsDirectory  )};
sub getCourseMacroDirectory		{ convertPath($macroDirectory  )};
sub getWebworkLogsDirectory		{ convertPath($webworkLogsDirectory)};

sub getCourseClasslistFile		{ convertPath("${coursesDirectory}$_[0]/templates/${classlistFilename}") };

sub getCourseKeyFile			{ convertPath("${coursesDirectory}$_[0]/DATA/${authDirectory}${keyFilename}") };
sub getCoursePasswordFile		{ convertPath("${coursesDirectory}$_[0]/DATA/${authDirectory}${passwordFilename}") };
sub getCoursePermissionsFile	{ convertPath("${coursesDirectory}$_[0]/DATA/${authDirectory}${permissionsFilename}") };
sub getCourseDatabaseFile 		{ convertPath("${coursesDirectory}$_[0]/DATA/${$database}") };

sub getCourseMOTDFile		    { convertPath("${coursesDirectory}$_[0]/templates/${course_motd_filename}") };
sub getSystemMOTDFile		    { convertPath("${mainDirectory}${system_motd_filename}") };

sub getWebworkCgiURL	{ $cgiWebworkURL };
sub getCourseHtmlURL	{ $htmlURL };
sub getCoursel2hURL		{ "${courseTempURL}l2h/" }
sub getCourseTempURL	{ $courseTempURL };   #defined in webworkCourse.ph
sub getDirDelim			{ $dirDelim };
sub getDelim			{ $delim };
sub getScoreFilePrefix	{ $scoreFilePrefix };
sub getScoring_log		{ $scoring_log };
sub getDash				{ $dash };
sub getDat				{ $dat };
sub getBbext			{ @dbext };
sub getStatusDrop		{ @statusDrop };

sub  getNumRelPercentTolDefault			{ $numRelPercentTolDefault };
sub  getNumZeroLevelDefault				{ $numZeroLevelDefault };
sub  getNumZeroLevelTolDefault			{ $numZeroLevelTolDefault };
sub  getNumAbsTolDefault				{ $numAbsTolDefault };
sub  getNumFormatDefault				{ $numFormatDefault };
sub  getFunctRelPercentTolDefault		{ $functRelPercentTolDefault };
sub  getFunctZeroLevelDefault			{ $functZeroLevelDefault };
sub  getFunctZeroLevelTolDefault		{ $functZeroLevelTolDefault };
sub  getFunctAbsTolDefault				{ $functAbsTolDefault };
sub  getFunctNumOfPoints				{ $functNumOfPoints };
sub  getFunctVarDefault					{ $functVarDefault };
sub  getFunctLLimitDefault				{ $functLLimitDefault };
sub  getFunctULimitDefault				{ $functULimitDefault };
sub  getFunctMaxConstantOfIntegration	{ $functMaxConstantOfIntegration };

sub  getLoginURL	{ $loginURL };

sub  getAllowDestroyRebuildProbSets	{ $allowDestroyRebuildProbSets };

sub getCourseEnvironment {
    die "getCourseEnvironment was called without specifying a course" unless $_[0];
	my $fullPath = convertPath("${coursesDirectory}$_[0]/$courseEnvironmentFile");
    require "$fullPath"
        || die "Can't find local environment file for
                 $fullPath\n";
}



### dump a (hopefully) descriptive error to the browser and quit


sub wwerror {
    my($title, $msg, $url, $label, $query_string) = @_;
    # <BODY BACKGROUND=\"$background_warn_url\">

    $msg = '' unless defined $msg;
    $url = '' unless defined $url;
    $label = '' unless defined $label;
    $query_string = '' unless defined $query_string;

    print "content-type: text/html\n\n
          <HTML><HEAD><TITLE>Error: $title</TITLE></HEAD>
          <BODY BGCOLOR = 'CCCCCC'>

          <H2>Error: $title</H2>
          <PRE>$msg\n
          </PRE>";
    if ($url) {
    print "<FORM ACTION=\"$url\">
           <INPUT TYPE=SUBMIT VALUE=\"$label\">
           </FORM>\n";
    }
    print "</BODY></HTML>";
    &log_error($title, $query_string);
    exit 1;
}

sub error {wwerror(@_);}        ##alias for wwerror


# return a (scalar) tip
sub tip {
    my ($tips, @tiplist);
    local($/) = undef;
    #undef $/; # slurp it all in
    open(TIPS, "$tipsFile") || &error("Can't open $tipsFile");
    $tips = <TIPS>;
    close(TIPS);

    # add any local tips to the list before we pick a random one
    if (-r "${templateDirectory}$tipsFilename") {
        open(TIPS, "${templateDirectory}$tipsFilename");
        $tips .= '%%' . <TIPS>;
        close(TIPS);
    }
    $/ = "\n"; # <> now reads until newline
    $tips =~ s/#.*?\n//mg;  # remove comments
    @tiplist = split(/%%/, $tips);
    return $tiplist[rand(@tiplist)];    # choose one at random
}

# return an array of tips
sub all_tips {
    my ($tip, $tips, @tiplist);

    local($/) = undef; # slurp it all in
    open(TIPS, "$tipsFile") || &error("Can't open $tipsFile");
    $tips = <TIPS>;
    close(TIPS);

    # add any local tips to the list before we pick a random one
    if (-r "${templateDirectory}$tipsFilename") {
        open(TIPS, "${templateDirectory}$tipsFilename");
        $tips .= '%%' . <TIPS>;
        close(TIPS);
    }
    $/ = "\n"; # <> now reads until newline
    $tips =~ s/#.*?\n//mg;  # remove comments
    @tiplist = split(/%%/, $tips);
    return @tiplist;
}


# begin Timing code
use Benchmark;
sub dateTime {
    my @timeArray = localtime(time);
    my $out = sprintf("%2.2d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
    $timeArray[5],$timeArray[4]+1,@timeArray[3,2,1,0]);
    $out;
}

#the ps system calls do not work on all systems, and are usually not
#necessary anyway. If you wish that information to be logged, simply
#uncomment the relevant lines, but be warned that they might need to
#be modified for your system
sub logTimingInfo {
    my ($beginTime,$endTime,$script,$course,$user,$remoteHost,$userAgent) = @_;
    $remoteHost = ""unless defined($remoteHost);
    $userAgent  = ""   unless defined($userAgent);
    open(TIMELOG, ">>${webworkLogsDirectory}timing_log") ||
			warn "*Unable to open timing log for writing:\n ${webworkLogsDirectory}timing_log. ";

	my $mem_string = '';
#     my $process_string = `ps -o vsz -p $$`;
#     $process_string =~ s/^\s*//;
#     $process_string =~ s/\s*$//;
#     my @process_string = split(/\s+/,$process_string);  # gets memory size
#     my $mem_string = " mem: ${process_string[1]}K";

	my $load_string = '';
#     my @load = split(/\n/,`ps -U wwhttpd -o state`);
#     my $load_string = " load: " . grep(/R/,@load);

    print TIMELOG $script,"\t",$course,"\t",&dateTime,"\t",
                  timestr( timediff($endTime,$beginTime), 'all' ),"\t",
                  "pid: $$ DBtie_tries: $Global::DBtie_tries" . $mem_string . $load_string,"\t",$user,"\t",
                  $remoteHost,"\t",$userAgent,"\n";
    close(TIMELOG);
}
# end Timing code


# handy routines for modules that wish to throw exceptions outside of the
# current package. (taken from Carp.pm)
#
# We'll want to remove this in final versions of WeBWorK.

sub log_error {
    my ($comment, $data) = @_;
    my $accessLog = convertPath("${webworkLogsDirectory}access_log");
    my $errorLog = convertPath("${webworkLogsDirectory}error_log");
    open(ACCESS, ">>$accessLog");
    open(ERROR, ">>$errorLog");
    print ACCESS "ERROR ($comment) ", scalar(localtime), ': ', &shortmess($data);
    print ERROR "ERROR ($comment) ", scalar(localtime), ': ', &shortmess($data);
    close(ACCESS);
    close(ERROR);
}

sub log_info {
    if( $Global::logAccessData == 1 ) {
		my ($comment, $data) = @_;
    	my $accessLog = convertPath("${webworkLogsDirectory}access_log");
    	open(LOG, ">>$accessLog") or warn "Can't open accessLog $accessLog";
    	print LOG "INFO ($comment) ", scalar(localtime), ': ',  &shortmess($data);
    	close(LOG);
	}
}


## converts full path names to to use the $dirDelim instead of /
sub convertPath {
    my ($path) = @_;
    warn "convertPath has been asked to convert an empty path<BR> |$path| at ", caller(),"<BR>" unless $path;
    $path =~ s|/|$dirDelim|g;

	$path;
}

# -----

BEGIN {
	sub PG_floating_point_exception_handler {       # 1st argument is signal name
		my($sig) = @_;
		print "Content-type: text/html\n\n<H4>There was a floating point arithmetic error (exception SIG$sig )</H4>--perhaps
		you divided by zero or took the square root of a negative number?
		<BR>\n Use the back button to return to the previous page and recheck your entries.<BR>\n";
		exit(0);
	}
	
	$SIG{'FPE'}  = \&PG_floating_point_exception_handler;

	sub PG_warnings_handler {
		my @input = @_;
		my $msg_string = longmess(@_);
		my @msg_array = split("\n",$msg_string);
		my $out_string = '';
		
		# Extra stack information is provided in this next block
		# If the warning message does NOT end in \n then a line 
		# number is appended (see Perl manual about warn function)
		# The presence of the line number is detected below and extra
		# stack information is added.
		# To suppress the line number and the extra stack information
		# add \n to the end of a warn message (in .pl files.  In .pg
		# files add ~~n instead
		
		if ($input[$#input]=~/line \d*\.\s*$/) {   
			$out_string .= "##More details: <BR>\n----"; 
			foreach my $line (@msg_array) {
				chomp($line);
				next unless $line =~/\w+\:\:/;
				$out_string .= "----" .$line . "<BR>\n";
			}
		}

		$Global::WARNINGS .="*  " . join("<BR>",@input) . "<BR>\n" . $out_string .
		                    "<BR>\n--------------------------------------<BR>\n<BR>\n";
		$Global::background_plain_url = $Global::background_warn_url;
		$Global::bg_color = '#FF99CC';  #for warnings -- this change may come too late
	}

	$SIG{__WARN__}=\&PG_warnings_handler;
	
	$SIG{__DIE__} = sub {
	    my $message = longmess(@_);
	    $message =~ s/\n/<BR>\n/;
	    my ($package, $filename, $line) = caller();
	    # use standard die for errors eminating from XML::Parser::Expat
	    # it uses a trapped eval which sometimes fails -- apparently on purpose
	    # and the error is handled by Expat itself.  We don't want
	    # to interfer with that.
	    
	    if ($package eq 'XML::Parser::Expat') {
	    	die @_;
	    }
	    #print  "$package $filename $line \n";
		print  
		"Content-type: text/html\r\n\r\n <h4>Software error</h4> <p>\n\n$message\n<p>\n
		Please inform the webwork meister.<p>\n
		In addition to the error message above the following warnings were detected:
		<HR>
		$Global::WARNINGS;
		<HR>
		It's sometimes hard to tell exactly what has gone wrong since the
		full error message may have been sent to
		standard error instead of to standard out.
		<p> To debug  you can
		<ul>
		<li> guess what went wrong and try to fix it.
		<li> call the offending script directly from the command line
		of unix
		<li> enable the debugging features by redefining
		\$cgiURL in Global.pm and checking the redirection scripts in
		system/cgi. This will force the standard error to be placed
		in the standard out pipe as well.
		<li> Run tail -f error_log <br>
		from the unix command line to see error messages from the webserver.
		The standard error output is being placed in the error_log file for the apache
		web server.  To run this command you have to be in the directory containing the
		error_log or enter the full path name of the error_log. <p>
		In a standard apache installation, this file is at /usr/local/apache/logs/error_log<p>
		In a RedHat Linux installation, this file is at /var/log/httpd/error_log<p>
		At Rochester this file is at /ww/logs/error_log.
		</ul>
		Good luck.<p>\n" ;
	};



}

###############
## Error message routines
###############

BEGIN {    #error message routines

	my $CarpLevel = 0;  # How many extra package levels to skip on carp.
	my $MaxEvalLen = 0; # How much eval '...text...' to show. 0 = all.
	
	sub longmess {
		my $error = shift;
		my $mess = "";
		my $i = 1 + $CarpLevel;
		my ($pack,$file,$line,$sub,$eval,$require);

		while (($pack,$file,$line,$sub,undef,undef,$eval,$require) = caller($i++)) {
			if ($error =~ m/\n$/) {
				$mess .= $error;
			}
			else {
				if (defined $eval) {
					if ($require) {
						$sub = "require $eval";
					}
					else {
						$eval =~ s/[\\\']/\\$&/g;
						if ($MaxEvalLen && length($eval) > $MaxEvalLen) {
							substr($eval,$MaxEvalLen) = '...';
						}
						$sub = "eval '$eval'";
					}
				}
				elsif ($sub eq '(eval)') {
					$sub = 'eval {...}';
				}

				$mess .= "\t$sub " if $error eq "called";
				$mess .= "$error at $file line $line\n";
			}

			$error = "called";
		}

		$mess || $error;
	}

	sub shortmess { # Short-circuit &longmess if called via multiple packages
		my $error = $_[0];  # Instead of "shift"
		my ($curpack) = caller(1);
		my $extra = $CarpLevel;
		my $i = 2;
		my ($pack,$file,$line);

		while (($pack,$file,$line) = caller($i++)) {
			if ($pack ne $curpack) {
				if ($extra-- > 0) {
					$curpack = $pack;
				}
				else {
					return "$error at $file line $line\n";
				}
			}
		}

		goto &longmess;
	}


}

###############
## End Error message routines
###############


1;  ## This line is required by perl
