Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??
package CA::AutoSys::Jil; use strict; use warnings; use Carp qw(confess); use vars qw(%AUTOSERV); use overload '""' => \&name; # Making assumptions here # Maybe we shouldn't set these $ENV{AUTOSERV} = '/usr/local/autosys/autosys'; $ENV{AUTOUSER} = '/usr/local/autosys/autouser'; # Hash of server names and the database server name # We make assumptions about the database name # (see use of AUTOSERV hash in from_server method) %AUTOSERV = ( dev_server_name => 'DEV', prod_server_name => 'PROD', ); sub autoserv { $AUTOSERV{$_[1]} } # List of valid sub_commands and attributes my @sub_commands = qw( alarm_if_fail auto_delete auto_hold avg_runtime box_failure box_name box_success box_terminator chk_files command condition date_conditions days_of_week delete_box delete_job description exclude_calendar heartbeat_interval insert_job job_load job_terminator job_type machine max_exit_success max_run_alarm min_run_alarm n_retrys override_job owner permission priority profile run_calendar run_window start_mins start_times std_err_file std_in_file std_out_file term_run_time timezone update_job watch_file watch_file_min_size watch_interval ); my ( %valid_cmd, %valid_old_cmd ); $valid_cmd{$_}++, $valid_old_cmd{"#old$_"}++ for @sub_commands; sub new { my $class = shift; my $self = bless {}, $class; my $opts = {}; $opts = pop @_ if @_ > 1 and ref $_[-1]; # This will catch certain edge cases (e.g. where @_ == 2) easily if ( @_ > 1 ) { for (@_) { return $self->read_array(@_, $opts) if /^\s*(?:insert|delete|upd +ate)_job:/; } } if ( @_ == 1 ) { my $arg = shift; return ref($arg) ? $self->read_file($arg, $opts) : ( $arg =~ /\n/ or $arg =~ /^\s*(?:insert|delete|update)_job:/ ) +? $self->read_text($arg, $opts) : ( $arg =~ m|/| or -f $arg ) ? $self->read_file($arg, $opts) : confess "Don't know how to get job $arg\n"; } if ( @_ == 2 ) { return $self->from_server(@_); } # This is probably unnecessary, but will nicely die for bad input # (e.g. missing insert/delete/update). $self->read_array(@_, $opts) if @_; return $self; } sub read_text { my $self = shift; $self = $self->new() unless ref $self; my $text = shift; my $opts = pop @_ if ref $_[-1]; $self->read_array( split(/\n/, $text), $opts); } sub read_file { my $self = shift; $self = $self->new() unless ref $self; my ($file, $opts) = @_; $opts ||= {}; my $fh; if ( ref($file) ) { $fh = $file; } else { open($fh, "<", $file) or confess "Can't open $file: $!"; } my @jil = <$fh>; close $fh; return $self->read_array(@jil, $opts); } sub read_array { my $self = shift; $self = $self->new() unless ref $self; my $opts = {}; $opts = pop @_ if ref $_[-1]; my $keep_old = $opts->{keep_old}; my $cmd_re = $keep_old ? qr/(?:#old)?\w+/ : qr/\w+/; my @input = @_; my ( $name, $full_text, @text, %jil, @cmd_list, $cmd_type ); for (@input) { next unless defined; s/^\s+//; s/\s+$//; $self->{FIXED_OWNER} = 1 if s/^#owner:/owner:/; s/@.*// if /^owner:/; $full_text .= "$_\n"; next unless /^($cmd_re):\s*(.*)/; my ( $cmd, $txt ) = ( $1, $2 ); push @text, $_; push @cmd_list, $cmd; if ( $cmd =~ /^(insert|update|delete)_job$/ ) { $cmd_type = $1; ($name, my ($job_type_cmd, $job_type)) = split " ", $txt; $job_type_cmd =~ s/:$// if $job_type_cmd; if ( $job_type_cmd ) { if ( $job_type_cmd eq 'job_type' ) { push @cmd_list, $job_type_cmd; $jil{$job_type_cmd} = $job_type; } else { confess "Can't determine job_type in $txt"; } } $txt = $name; } $jil{$cmd} = $txt; } confess "No name found for jil" unless $name; @$self{qw(NAME TEXT CMD CMD_LIST CMD_TYPE FULLTEXT)} = ( $name, \@te +xt, \%jil, \@cmd_list, $cmd_type, $full_text ); return $self; } sub fixed_owner { return $_[0]->{FIXED_OWNER} } sub name { return $_[0]->{NAME} } sub cmd_type { return $_[0]->{CMD_TYPE} } sub status { my $self = shift; unless ( @_ ) { confess "Must supply server for $self" unless $self->{SERVER}; $self->get_status() unless $self->{STATUS}; return $self->{STATUS}; } my $name = $self->name(); my $server = shift; my $jil = ref($self)->from_autosys($name, $server); $jil->get_status(); return $jil->{STATUS}; } sub from_server { my $self = shift; $self = $self->new() unless ref $self; my ( $jil, $server ) = @_; $jil =~ s/\.jil$//i; confess "Invalid jil name: $jil" unless $jil =~ /^[\w-]+$/; my $srv = $self->autoserv($server) or confess "$server is not a know +n AutoSys server!\n"; my $fh = $self->exec_cmd( '/usr/local/autosys/autosys/bin/autorep', '-J', $jil, '-D', "SQL_AUTOSYS_$srv:autosys", '-q', '-L', 0 ); $self->{SERVER} = $server; $self->read_file($fh); $self->{FIXED_OWNER} = 0; return $self; } sub box_dependencies { my $self = shift; unless ( @_ ) { confess "Must supply server for $self" unless $self->{SERVER}; $self->get_box_dependencies() unless $self->{DEPENDENCIES}; return $self->{DEPENDENCIES}; } my $name = $self->name(); my $server = shift; my $jil = ref($self)->from_autosys($name, $server); $jil->get_box_dependencies(); return $jil->{DEPENDENCIES}; } sub get_box_dependencies { my $self = shift; my $server = @_ ? shift() : $self->{SERVER}; return [] unless $self->job_type() eq 'b'; my $srv = $self->autoserv($server) or confess "$server is not a know +n AutoSys server!\n"; my $fh = $self->exec_cmd( '/usr/local/autosys/autosys/bin/job_depends', '-J', $self->name(), '-D', "SQL_AUTOSYS_$srv:autosys", '-d', '-L1', ); my @dep_jobs; my $got_this_job; my $get_jobs; my $name = $self->name(); my $name_re = qr/^\Q$name\E\s/; local $_; while (<$fh>) { $got_this_job++ if /$name_re/; next unless $got_this_job; next unless $get_jobs++; /^\s+([\w-]+)\s/ or next; push @dep_jobs, $1; } $self->{DEPENDENCIES} = \@dep_jobs; } sub dependencies { my $self = shift; unless ( @_ ) { confess "Must supply server for $self" unless $self->{SERVER}; return $self->status->dependent_jobs(); } my $name = $self->name(); my $server = shift; my $jil = ref($self)->from_autosys($name, $server); return $jil->status->dependent_jobs(); } sub get_status { my $self = shift; my $server = @_ ? shift() : $self->{SERVER}; my $srv = $self->autoserv($server) or confess "$server is not a know +n AutoSys server!\n"; my $fh = $self->exec_cmd( '/usr/local/autosys/autosys/bin/job_depends', '-J', $self->name(), '-D', "SQL_AUTOSYS_$srv:autosys", '-c', ); my ( $name, $status, $date_cond, $start_cond, $dep, @cond, @dep_job +); local $_; while (<$fh>) { if ( my $cur_stat = /^Job Name/.../^[\w]+/ ) { if ( $cur_stat =~ /E/ ) { ( $name, $status, $date_cond, $start_cond, $dep ) = split; } } # Get Condition Status if ( my $cond_stat = /^\s*Atomic Condition/.../^(?:\s*|___________ +______.*)$/ ) { if ( $cond_stat > 2 and $cond_stat !~ /E/ ) { my ( $cond, $cond_status, $t_f ) = split; unless ( $cond =~ /^(\w+)\(([\w-]+)\)/ ) { confess "Invalid condition $cond for $self\n"; } my ( $cond_req, $cond_job ) = ( $1, $2 ); my $met = ( $t_f eq "T" ); push @cond, bless { JOB_NAME => $cond_job, REQ_STATUS => $cond_req, CURRENT => $cond_status, MET => $met, }, "CA::AutoSys::Jil::ConditionStatus"; } } if ( my $dep_stat = /^\s*Dependent Job Name/.../^(?:\s*|__________ +_______.*)$/ ) { if ( $dep_stat > 2 and $dep_stat !~ /E/ ) { my ( $dep_job, $dep_cond ) = split; $dep_cond =~ s/\(.*//; push @dep_job, bless { JOB_NAME => $dep_job, REQ_STATUS => $dep_cond, }, "CA::AutoSys::Jil::DependentJob"; } } } close $fh; confess "$self does not exist on $server" unless $name; $self->{STATUS} = bless { CURRENT => $status, DATE_COND => $date_cond, CONDITIONS => \@cond, DEP_JOBS => \@dep_job, }, "CA::AutoSys::Jil::Status"; } sub exec_cmd { my $self = shift; my @cmd = @_; my $pid = open(my $pipe, "-|"); die "Can't fork: $!" unless defined $pid; unless ($pid) { close STDERR; exec @cmd; die "Could not exec @cmd: $!"; } return $pipe; } # Take source and target jils and generate # an update command sub mk_update { my ( $from, $to, $rev ) = @_; ( $from, $to ) = ( $to, $from ) if $rev; my @from_cmd = @{$from->{CMD_LIST}}; my @to_cmd = @{$to->{CMD_LIST}}; my $from_cnt = @from_cmd; my $to_cnt = @to_cmd; my %from_val = %{$from->{CMD}}; my %to_val = %{$to->{CMD}}; ( my $from_name = $from_val{insert_job} ) =~ s/\s.*//; ( my $to_name = $to_val{insert_job} ) =~ s/\s.*//; confess "$from_name != $to_name!\n" if $from_name ne $to_name;; my ($to_idx, $from_idx) = (1,1); my @upd_txt; while ( $from_idx < $from_cnt or $to_idx < $to_cnt ) { my $from_cmd = ( $from_idx < $from_cnt ) ? $from_cmd[$from_idx] : +''; my $to_cmd = ( $to_idx < $to_cnt ) ? $to_cmd[$to_idx] : ''; my $from_txt = exists($from_val{$from_cmd}) ? $from_val{$from_cmd} + : ''; my $to_txt = exists($to_val{$to_cmd}) ? $to_val{$to_cmd} : ''; my $from_sp = length($from_txt) ? " " : ""; my $to_sp = length($to_txt) ? " " : ""; if ( $from_cmd eq $to_cmd ) { if ( $from_txt eq $to_txt ) { $from_idx++; $to_idx++; next; } if ( $from_cmd eq 'owner' ) { warn "Warning: Change in owner for $from_name\n"; } my $upd_to = "$to_cmd:$to_sp$to_txt"; my $upd_from = "#old$from_cmd:$from_sp$from_txt"; push @upd_txt, $upd_to, $upd_from; $from_idx++; $to_idx++; next; } # We are deleting a command if ( exists $from_val{$to_cmd} or $to_idx >= $to_cnt ) { if ( exists $to_val{$from_cmd} ) { warn "Warning: Commands out of order in $from_name--manual upd +ate required\n"; last; } if ( $from_cmd eq 'owner' ) { warn "Warning: Can't delete owner from $from_name\n"; $from_idx++; next; } elsif ( $from_cmd eq 'date_conditions' ) { push @upd_txt, "$from_cmd: 0", "#old$from_cmd: $from_txt"; $from_idx++; next; } push @upd_txt, "$from_cmd:", "#old$from_cmd: $from_txt"; $from_idx++; next; } # We are inserting a command if ( exists $to_val{$from_cmd} or $from_idx >= $from_cnt ) { if ( exists $from_val{$to_cmd} ) { # We should never get here, but just for consistency warn "Warning: Commands out of order in $to_name--manual updat +e required\n"; last; } if ( $to_cmd eq 'owner' ) { warn "Warning: Inserting owner in $to_name\n"; } push @upd_txt, "$to_cmd: $to_txt", "#old$to_cmd:"; $to_idx++; next; } # Assume there is an insert if ( $to_cmd eq 'owner' ) { warn "Warning: Insert owner in $to_name\n"; $to_idx++; next; } push @upd_txt, "$to_cmd: $to_txt", "#old$to_cmd:"; $to_idx++; next; } if (!@upd_txt) { warn "No changes detected in $to_name\n"; return ''; } return join '', map { "$_\n" } "update_job: $to_name", @upd_txt; } # Compare the jils line by line and # return differences sub diff { my ( $from, $to ) = @_; unless ( ref $to ) { $to = ref($from)->read_text( $to, { keep_old => 1 } ); } my @from_cmd = @{$from->{CMD_LIST}}; my @to_cmd = @{$to->{CMD_LIST}}; my %from_val = %{$from->{CMD}}; my %to_val = %{$to->{CMD}}; my @diffs; my %seen; for my $cmd ( @from_cmd ) { my $from = "$cmd: $from_val{$cmd}"; my $to = (exists $to_val{$cmd}) ? "$cmd: $to_val{$cmd}" : ''; push @diffs, $from, $to if $from ne $to; $seen{$cmd}++; } for my $cmd ( @to_cmd ) { next if $seen{$cmd}++; my $from = (exists $from_val{$cmd}) ? "$cmd: $from_val{$cmd}" : '' +; my $to = "$cmd: $to_val{$cmd}"; push @diffs, $from, $to if $from ne $to; } return @diffs; } sub validate { my $jil = shift; my @errors; for my $cmd ( @{$jil->{CMD_LIST}} ) { push @errors, "$jil has invalid command($cmd)" and next unless $valid_cmd{$cmd} or $valid_old_cmd{$cmd}; if ( my $val = $jil->can("validate_$cmd") ) { next unless defined &$val; push @errors, $jil->$val(); } } return @errors; } sub validate_insert_job { my $self = shift; my $job_name = $self->name(); my $cmd_type = $self->cmd_type(); my @errors; push @errors, "$job_name failed name > 30 chars" if length($job_name +) > 30; push @errors, "$job_name failed job_type(missing)" if $cmd_type eq ' +insert' and ! defined($self->job_type()); return @errors; } { no warnings 'once'; *validate_update_job = *validate_delete_job = \&validate_insert_job; } sub validate_job_type { my $self = shift; my $job_name = $self->name(); my $job_type = $self->job_type(); my @errors; push @errors, "$job_name failed job_type($job_type)" unless $job_typ +e =~ /^[cfb]$/; return @errors; } sub validate_box_name { my $self = shift; my $job_name = $self->name(); my $box_name = $self->box_name(); my @errors; push @errors, "$job_name failed box_name($box_name) > 30 chars" if l +ength($box_name) > 30; return @errors; } for my $sub_cmd (@sub_commands) { no strict 'refs'; *$sub_cmd = sub { my $self = shift; return $self->{CMD}{$sub_cmd}; }; } sub text { my $self = shift; my $type; if ( @_ ) { $type = shift; unless ( $type ) { return $self->{FULLTEXT}; } } my $txt; if ( $type ) { $txt .= "/* ----------------- $self->{NAME} ----------------- */\n +\n"; } $txt .= "$_: $self->{CMD}{$_}" . ( ( $_ eq "insert_job" ) ? " " : +"\n" ) for @{$self->{CMD_LIST}}; return $txt; } sub split_file { my $class = shift; my @jils; my $splitter = $class->splitter(@_); my $jil; push @jils, $jil while $jil = $splitter->next_jil(); return @jils; } sub splitter { my $class = shift; @_ > 0 or confess "Must supply file name or file handle to spltter\n +"; my $file = shift; return CA::AutoSys::Jil::Splitter->new($file, $class); } package CA::AutoSys::Jil::Splitter; use Carp qw(confess); sub new { my $class = shift; my $file = shift or confess "Must supply file name or file handle\n" +; my $proto = shift(@_) || 'CA::AutoSys::Jil'; $proto = ref($proto) if ref($proto); my $fh; if ( ref($file) ) { $fh = $file; } else { open($fh, "<", $file) or confess "Can't open $file: $!"; } bless { FH => $fh, BUFFER => [], GOT_COMMENT => \my $got_comment, GOT_COMMAND => \my $got_command, SAVE => \my $save, PROTO => $proto, }, $class; } sub next_jil { my $self = shift; my ( $fh, $buffer, $got_comment, $got_command, $save, $proto ) = @$self{qw( FH BUFFER GOT_COMMENT GOT_COMMAND SAVE PROTO )}; my $process_previous; local $_; # Process the previously saved jil when we find either # a comment or a new job command. while (<$fh>) { if ( m!^\s*/\*! ) { $$got_comment = 1; $process_previous = 1; } if ( /^(?:insert|update|delete)_job:/ ) { $process_previous = 1 unless $$got_comment; $$got_command++; $$got_comment = 0; } } continue { my $is_eof = eof($fh); if ( $process_previous or $is_eof ) { push @$buffer, $_ if $is_eof; # Only process if something is in the buffer and # it contains a command. if ( @$buffer and $$got_command ) { $$got_command--; my $jil = $proto->read_array( @$buffer, { keep_old => 1 }); @$buffer = $is_eof ? () : ($_); $$save = ! $is_eof; return $jil; } $process_previous = 0; $$save = ! $is_eof; } push @$buffer, $_ if $$save; } return; } package CA::AutoSys::Jil::Status; use overload '""' => \&current; sub current { $_[0]->{CURRENT} } sub date_cond { $_[0]->{DATE_COND} } sub conditions { $_[0]->{CONDITIONS} } sub dependent_jobs { $_[0]->{DEP_JOBS} } package CA::AutoSys::Jil::ConditionStatus; sub job_name { $_[0]->{JOB_NAME} } sub req_status { $_[0]->{REQ_STATUS} } sub current { $_[0]->{CURRENT} } sub met { $_[0]->{MET} } package CA::AutoSys::Jil::DependentJob; sub job_name { $_[0]->{JOB_NAME} } sub req_status { $_[0]->{REQ_STATUS} } 1; __END__ =head1 NAME CA::AutoSys::Jil - Module for Jil processing =head1 SYNOPSIS use CA::AutoSys::Jil; $jil = CA::AutoSys::Jil->new(@lines|$text|$file_path|$fh|$jil_name +,$server, [\%options]) $jil->read_file($file_name | $fh, [\%options]) $jil->read_text($text, [\%options]) $jil->read_array(@array, [\%options]) $jil->from_server($jil, $server) $jil->name() $jil->cmd_type() $jil->fixed_owner() $jil1->mk_update($jil2, [$reverse]) $jil1->diff($jil2) $jil->text([$type]) $splitter = CA::AutoSys::Jil->splitter($file|$fh) $jil = $splitter->next_jil() @jils = CA::AutoSys::Jil->split_file($file|$fh) $status = $jil->status([$server]); @dependencies = @{ $jil->box_dependencies() }; @dependent_jobs = @{ $jil->dependencies() }; $current_status = $status->current(); @conditions = @{ $status->conditions() }; @dependent_jobs = @{ $status->dependent_jobs() }; $job_name = $condition->job_name(); $required_status = $condition->req_status() $current_status = $condition->current(); $condition_met = $condition->met(); $job_name = $dependent_job->job_name(); $required_status = $dependent_job->req_status(); =head1 DESCRIPTION Implements methods to facilitate jil processing =head1 METHODS =over 4 =item new () Creates a new CA::AutoSys::Jil object. Also calls one of the read_* or + from_* methods depending on the arguments. The logic below may seem complex, +but it makes it easy to make the module do the "right" thing. If there is more than one argument, and the last argument is a reference, we assume the last argument is an options hash reference, and it is not counted as an argument below. If there is only one argument: and it is a reference, we call read_file() and it contains newlines, we call read_text() and it contains any "/", we call read_file() If there are two arguments: and the second argument is a known autosys server, we call from_serv +er() If there are more than two arguments, we call read_array() Otherwise, you must call one of the read_* or from_* methods yourself. The last argument is an optional hash reference, and if present is pas +sed along to the read_* methods. Common option(s) for the read methods are: =head2 Options =over 4 =item keep_old If this option is true, then lines that begin with "#old" are also considered "commands" (Default: false). =back =item read_array (@lines, [\%options]) Reads jil from an array. =item read_text ($text, [\%options]) Reads jil from a text string. =item read_file ($file_name|$fh, [\%options]) Reads jil from a file name or if the argument is a reference, an already opened file handle. =item from_server ($jil_name, $server) Gets jil from autosys server. Also determines current status of job and the conditions in the job. =item box_dependencies ([$server]) If the job is a box, returns an array reference to the names of all the jobs in the box. If the job is not a box, returns a reference to an empty array. The server argument is optional if from_server() was used to create th +e object. =item dependencies ([$server]) Returns a reference to an array of dependency objects containing all the jobs dependent on this job. This is the same as getting dependent jobs from a status object. The server argument is optional if from_server() was used to create th +e object. =item status ([$server]) Returns a jil status object containing the status of the current job and all jobs in the condition from the given server. $server is optional if from_server() was used to create the object. =head2 Status Methods These are the methods that may be used on a jil status object: =over 4 =item current () Returns the current status of the job from the status object. =item conditions () Returns a list of condition status objects containing the status of each of the conditions in the jil object. =item dependent_jobs () Returns a list of dependency status objects containing the jobs dependent on this job. =back =head2 Condition Status Methods These are the methods that may be used on a condition status object: =over 4 =item job_name () Returns the job name or the name of the autosys variable in the condit +ion. =item req_status () Returns the status of the job required to satisfy the condition (or 'VALUE' if the condition contains a variable). =item current () Returns the current status of the job or variable in the condition. =item met () Returns a boolean indicating whether or not the condition has been me +t. =back =head2 Dependency Status Methods These are the methods that may be used on a dependency status object: =over 4 =item job_name () The name of the dependent job. =item req_status () Returns the required status of the job that this job depends on. =back =item mk_update ($target_jil, [$reverse]) Compares the original jil object to the target jil passed in, and returns an update_job command which will transform the original to the target jil. Or if reverse option is true, returns an update command to transform the other way around. =item validate () Makes sure every sub_command and attribute is valid, and calls all existing validate_* methods for every sub command and attribute that exists in the jil. Checks for correctness and various current jil policies. See L<Validation Methods> section below. =item splitter ($file|$fh) Initializes a jil splitter object from a file name or a file handle, from which jils will be read. Called as a class method, e.g.: my $splitter = CA::AutoSys::Jil->splitter($file); =over 4 =item next_jil () Returns the next jil from the jil splitter object, or undefined if there are no more jils. Typically used in a while loop, e.g.: while ( my $jil = $splitter->next_jil() ) { # Process $jil ... } =back =item split_file ($file|$fh) Reads jils from a file or file handle, and returns a list of jil objec +ts. Called as a class method, e.g.: my @jils = CA::AutoSys::Jil->split_file($file); =item diff ($target_jil) Compares the original jil against another jil, and returns a list of the differences as an array, where the lines in the array alternate + between the original and the target jil. Does not try to do a 'diff' style output, + just compares every command line from beginning to end and returns any line +s that differ. The target_jil may be an CA::AutoSys::Jil object or just the text of a + jil. =item text ([$type]) Returns the text of the insert_jil command. If called with no arguments, then only the jil command (e.g. comments will be omitted) will be returned. If called with a true argument, then a header comment line will be prepended to the text. If called with a false argument, then the original text as read from the source (minus leading and trailing whitespace) will be returned. =item name () The name of the jil will be returned. If the jil object is used inside double quotes, this method will be called, and the object will be stringified to this value. =item cmd_type () The type of command (insert/delete/update) will be returned (same valu +e as job_type() but without the trailing "_job"). =item fixed_owner () When the jil is read in, any hash("#") is stripped from the beginning +of the "owner:" line, and a flag is set if the hash was present before being stripped (except when read in via the from_server +() method). This method returns the status of that flag. =item Jil Sub Commands/Attributes The name of any jil sub-command or attribute may be used as a method a +nd the value of the sub-command or attribute will be returned (or undef i +f it does not exist). =back =head2 Validation Methods Current validate methods which can be called separately or may all be called with the validate() method: =over 4 =item validate_insert_job () =item validate_update_job () =item validate_delete_job () =item validate_box_name () Make sure that any job or box name is 30 characters or less. Make sure insert_job includes a job_type. =item validate_job_type () Make sure job type is c, f, or b. =back =head1 AUTHOR runrig =cut

In reply to CA::AutoSys::Jil module by runrig

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (4)
As of 2024-04-25 05:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found