Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.92k stars 551 forks source link

perl bug: loop variable is undef in foreach loop #6163

Closed p5pRT closed 21 years ago

p5pRT commented 21 years ago

Migrated from rt.perl.org#19154 (status was 'resolved')

Searchable as RT19154$

p5pRT commented 21 years ago

From aw464@osfn.org

I was trying to create a perl script to search through all my fonts directories\, check for various errors\, and print out a small sample of each of my fonts (also I was fantasizing about automatically fixing errors).

In my script\, there is a foreach loop. The foreach loop should run once for each directory name in the list\, with the loop variable set to each directory name in turn. The foreach loop runs the correct number of times\, and the loop variable is usually set to the correct directory name\, but for one run of the foreach loop\, the loop variable is set to undef instead of the correct directory name\, which I think is a bug in perl.

I run the script with standard error redirected to a file. In the middle of the error file\, there are the following nine lines​:

Use of uninitialized value in chdir at ./listfonts line 78. Use of uninitialized value in concatenation (.) at ./listfonts line 79.

errors in directory '' unable to read file fonts.dir unable to read file Fontmap Use of uninitialized value in concatenation (.) at ./listfonts line 135.

errors in directory '/dos/windows/system'

These error messages show that $SelectedDirectory is set to undef. This occurs before $SelectedDirectory is set to '/dos/windows/system'; therefore $SelectedDirectory is set to undef when $SelectedDirectory should be set to '/usr/share/fonts/default/Type1'. I think this is a bug in perl.

I can activate and deactivate the bug by making a small change elsewhere in the script. This is explained in the comments in the script.

I am not asking for help. I am submitting this script because I think it might help you to debug perl.

I do not know if this bug can be duplicated without the exact same directories and files as I have.

Here is my script\, which I call listfonts #!/usr/bin/perl -w use integer;

$FontSize = 6; $TopOfPage = 790; $BottomOfPage = 102; $LeftMargin = 16; $DefaultFont = '/XXXXXXXX'; @​FontPath = ('/usr/share/fonts/default/ghostscript'\,   '/usr/X11R6/lib/X11/fonts/misc'\,   '/usr/X11R6/lib/X11/fonts/Type1'\,   '/usr/X11R6/lib/X11/fonts/CID'\,   '/usr/X11R6/lib/X11/fonts/local'\,   '/usr/X11R6/lib/X11/fonts/latin2/Type1'\,   '/usr/X11R6/lib/X11/fonts/Speedo'\,   '/usr/share/fonts/default/TrueType'\,   '/usr/share/fonts/default/Type1'\,   '/dos/windows/system'\,   '/windows/windows/fonts'\,   '/usr/share/AbiSuite/fonts'\,   '/usr/staroffice-5.2/share/fonts/75dpi'\,   '/usr/staroffice-5.2/share/fonts/75dpi/bdf'\,   '/usr/staroffice-5.2/share/fonts/type1'\,   '/usr/wordperfect-8/shlib10/fonts'\,   '/usr/share/fonts/ja/TrueType');

#print @​FontPath;exit;

$NextLine = $TopOfPage; print('%!PS'\,"\n$DefaultFont findfont $FontSize scalefont setfont\n");

@​FileNamesFromAllFontmaps = (); @​FontNamesFromAllFontmaps = (); @​AllFontFileNames = ();

# some postscript font names are given in # /usr/share/ghostscript/6.51/lib/Fontmap.GS if ( open(INPUT\,"\< /usr/share/ghostscript/6.51/lib/Fontmap.GS") ) {   while (\) {   chomp($_);   if ( m/(\S{1\,})\s{1\,}\((.{1\,})\)/ ) {   push(@​FileNamesFromAllFontmaps\,$2);   push(@​FontNamesFromAllFontmaps\,$1);   }   }   close(INPUT);   } #for ( @​FontPath ) { $SelectedDirectory = $_; # if the previous line is not commented and the next line is # commented\, then the bug bites. if the previous line is # commented and the next line is not commented\, the bug does # not bite. Note that the bug does not bite here\, the bug bites below. # Changing these lines seems to activate and deactivate the bug. foreach $SelectedDirectory ( @​FontPath ) {   if ( ! chdir($SelectedDirectory) ) { &zNoChDir; next }   if ( open(INPUT\,"\< Fontmap") ) {   while (\) {   chomp($_);   if ( m/(\S{1\,})\s{1\,}\((.{1\,})\)/ ) {   push(@​FileNamesFromAllFontmaps\,$2);   push(@​FontNamesFromAllFontmaps\,$1);   }   }   close(INPUT);   }   @​AllFontFileNames = (@​AllFontFileNames\,glob('*.{gsf\,pfa\,pfb\,ttf}'));   }

# The bug bites in the next line. The foreach loop always runs # the correct number of times. If the bug bites\, then when # $SelectedDirectory is supposed to be set to # /usr/share/fonts/default/Type1\, then $SelectedDirectory is # actually set to undef\, which means that perl displays three # warnings about using an undefined variable\, and the script # checks the home directory instead of directory # /usr/share/fonts/default/Type1 foreach $SelectedDirectory ( @​FontPath ) {   if ( ! chdir($SelectedDirectory) ) { &zNoChDir; next }   print(STDERR "\nerrors in directory '$SelectedDirectory'\n");   @​FontFileNames = glob('*.{gsf\,pfa\,pfb\,ttf}');   @​FileNamesFromFontsdir = ();   @​FontNamesFromFontsdir = ();   @​FileNamesFromFontmap = ();   @​FontNamesFromFontmap = ();   for ( glob ('*.{gsf\,pfa\,pfb\,ttf}.gz') ) { &zFontCompressed($_) }

  # read fonts.dir   if ( open(INPUT\,"\< fonts.dir") ) {   while (\) {   chomp($_);   if ( m/(\S{1\,})\s{1\,}(.{1\,})/ ) {   if ( -f $1 ) {   push(@​FileNamesFromFontsdir\,$1);   push(@​FontNamesFromFontsdir\,$2);   }   else {   print(STDERR "fonts.dir includes '$1'\, which does not exist\n");   }   }   }   close(INPUT);   }   else { &zNoFontsdir }

  # read Fontmap   if ( open(INPUT\,"\< Fontmap") ) {   while (\) {   chomp($_);   if ( m/(\S{1\,})\s{1\,}\((.{1\,})\)/ ) {   if ( -f $2 ) {   push(@​FileNamesFromFontmap\,$2);   push(@​FontNamesFromFontmap\,$1);   }   else {   print(STDERR "Fontmap includes '$2'\, which does not exist\n");   }   }   }   close(INPUT);   }   else { &zNoFontmap }

  # if we are close to the bottom of the page\, start a new page   if ( ($NextLine - (3 * $FontSize)) \< $BottomOfPage ) {   print("showpage\n");   $NextLine = $TopOfPage;   }   # if we are at the top of the page\, move down one line   # if we are NOT at the top of the page\, move   # down two lines so a skip a blank line will seperate   # this directory from the previous directory   if ( $NextLine == $TopOfPage )   { $NextLine = $NextLine - $FontSize }   else { $NextLine = $NextLine - ($FontSize * 2) }   print("$LeftMargin $NextLine moveto "\,   "(fonts in directory '$SelectedDirectory'​:) show\n");

  for ( @​FontFileNames ) {   &checkForDuplicateFonts($_);   &calculatePositionOfNextLine;   print("($SelectedDirectory/$_) findfont $FontSize scalefont setfont "\,   "$LeftMargin $NextLine moveto (ABCDEFghijkl) show "\,   "$DefaultFont findfont $FontSize scalefont setfont "\,   "( $_ "\, &postscriptname($_)\,' '\,&xwindowsname($_)\,") show\n");   }

  }

print("showpage\n");

sub calculatePositionOfNextLine { $NextLine = $NextLine - $FontSize; if ( $NextLine \< $BottomOfPage ) {   print("showpage\n");   $NextLine = $TopOfPage - $FontSize;   } }

sub checkForDuplicateFonts { # @​AllFontFileNames my $NA = 0; my $NB = -1; while ( $NA \< $#AllFontFileNames ) {   if ( $AllFontFileNames[$NA] eq $_[0] ) { $NB = $NB + 1 }   $NA = $NA + 1;   } if ( $NB == 1 ) { print(STDERR "font file '$_[0]' conflicts with a font file named '$_[0]'   in 1 other directory ") } if ( $NB > 1 ) { print(STDERR "font file '$_[0]' conflicts with font files named   '$_[0]' in $NB other directories ") } if ( $_[0] =~ m/(\S{1\,}\.pf)a/ ) {   $NA = 0; $NB = 0; my $SA = $1 . 'b';   if ( -f $SA ) { print(STDERR "current directory has font files named '$_[0]' and '$SA'\,   which are probably the same font "); }   while ( $NA \< $#AllFontFileNames ) {   if ( $AllFontFileNames[$NA] eq $SA ) { $NB = $NB + 1 }   $NA = $NA + 1;   }   if ( $NB > 0 ) { print(STDERR "font file '$_[0]' conflicts with $NB font files named   '$SA' in various directories ") }   } if ( $_[0] =~ m/(\S{1\,}\.pf)b/ ) {   $NA = 0; $NB = 0; my $SA = $1 . 'a';   while ( $NA \< $#AllFontFileNames ) {   if ( $AllFontFileNames[$NA] eq $SA ) { $NB = $NB + 1 }   $NA = $NA + 1;   }   if ( $NB > 0 ) { print(STDERR "font file '$_[0]' conflicts with $NB font files named   '$SA' in various directories ") }   } }

sub postscriptname { my $NA = 0; while ( $NA \< $#FontNamesFromFontmap ) {   if ( $_[0] eq $FileNamesFromFontmap[$NA] )   { return($FontNamesFromFontmap[$NA]) }   $NA = $NA + 1;   } print(STDERR "$_[0] is not listed in Fontmap\n"); # if the font file was not listed in the Fontmap in the current directory\, # see if the font file was listed in one of the other Fontmaps $NA = 0; while ( $NA \< $#FontNamesFromAllFontmaps ) {   if ( $_[0] eq $FileNamesFromAllFontmaps[$NA] )   { return($FontNamesFromAllFontmaps[$NA]) }   $NA = $NA + 1;   } return(''); }

sub xwindowsname { my $NA = 0; if ( $_[0] =~ m/\.gsf$/ ) { return('') } while ( $NA \< $#FontNamesFromFontsdir ) {   if ( $_[0] eq $FileNamesFromFontsdir[$NA] )   { return($FontNamesFromFontsdir[$NA]) }   $NA = $NA + 1;   } print(STDERR "$_[0] is not listed in fonts.dir\n"); return(''); }

sub zFontCompressed { print(STDERR "font file '$_[0]' should be ungzipped so that it can be used by ghostscript "); }

sub zNoChDir { print(STDERR " unable to change to directory '$SelectedDirectory' ") }

sub zNoFontsdir { print(STDERR "unable to read file fonts.dir "); }

sub zNoFontmap { print(STDERR "unable to read file Fontmap "); }

__END__

This script is supposed to print a small sample of all fonts on your computer\, and to check for errors in the font configuration.

First you need to make a list of all directories which include fonts\, and add all font directories to the xwindows font path\, and to the ghostscript font path. Set @​FontPath to the list of all your font directories.

$TopOfPage\, $BottomOfPage\, and $LeftMargin may need to be adjusted for your printer. To make the top margin larger\, decrease $TopOfPage. To make the bottom margin larger\, increase $BottomOfPage. To make the left margin larger\, increase $LeftMargin. These numbers are in postscript units\, which is about 2.8 per millimeter\, or about 78 per inch.

$DefaultFont can be set to the postscript name of a font\, which is usually a string which begins with '/'\, includes more capital letters than small letters\, and may include '-'\, but does not include ' '. Or $DefaultFont can be set to the name of a font file which is in the ghostscript path\, enclosed in parentheses\, like this​:   $DefaultFont = '(times.ttf)'; I suggest that you should set $DefaultFont to an invalid font\, so that ghostscript will substitute its default font. Then you can look at the printout and see if any of the fonts look the same as the default font. If any fonts look the same as the default font\, that means that ghostscript could not find or could not use that font\, and substituted the default font. (Or maybe it means that font is the default font.)

You probably want to run this script with standard output redirected to one file\, and standard error redirected to a different file\, like this​:   listfonts >fonts.ps 2>font_errors Then you can display fonts.ps with gv\, or else print fonts.ps.

I am assuming that ghostscript is your printer driver. If ghostscript is not your printer driver\, you need to figure out how to make fonts available to your printer or printer driver.

fonts.dir is the font index for xwindows. Fontmap is the font index for ghostscript.

I am running redhat 7.2 with a custom 2.4.19 kernel. I have also installed various other rpm packages\, which might include other perl modules\, which might be intended for other versions of perl.

output of perl -V​: Summary of my perl5 (revision 5.0 version 6 subversion 0) configuration​:   Platform​:   osname=linux\, osvers=2.4.6-3.1enterprise\, archname=i386-linux   uname='linux stripples.devel.redhat.com 2.4.6-3.1enterprise #1 smp tue jul 24 14​:03​:17 edt 2001 i686 unknown '   config_args='-des -Doptimize=-O2 -march=i386 -mcpu=i686 -Dcc=gcc -Dcccdlflags=-fPIC -Dinstallprefix=/usr -Dprefix=/usr -Darchname=i386-linux -Dd_dosuid -Dd_semctl_semun -Di_db -Di_ndbm -Di_gdbm -Di_shadow -Di_syslog -Dman3ext=3pm -Uuselargefiles'   hint=recommended\, useposix=true\, d_sigaction=define   usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef   useperlio=undef d_sfio=undef uselargefiles=undef   use64bitint=undef use64bitall=undef uselongdouble=undef usesocks=undef   Compiler​:   cc='gcc'\, optimize='-O2 -march=i386 -mcpu=i686'\, gccversion=2.96 20000731 (Red Hat Linux 7.1 2.96-96)   cppflags='-fno-strict-aliasing -I/usr/local/include'   ccflags ='-fno-strict-aliasing -I/usr/local/include'   stdchar='char'\, d_stdstdio=define\, usevfork=false   intsize=4\, longsize=4\, ptrsize=4\, doublesize=8   d_longlong=define\, longlongsize=8\, d_longdbl=define\, longdblsize=12   ivtype='long'\, ivsize=4\, nvtype='double'\, nvsize=8\, Off_t='off_t'\, lseeksize=4   alignbytes=4\, usemymalloc=n\, prototype=define   Linker and Libraries​:   ld='gcc'\, ldflags =' -L/usr/local/lib'   libpth=/usr/local/lib /lib /usr/lib   libs=-lnsl -ldl -lm -lc -lcrypt   libc=/lib/libc-2.2.4.so\, so=so\, useshrplib=false\, libperl=libperl.a   Dynamic Linking​:   dlsrc=dl_dlopen.xs\, dlext=so\, d_dlsymun=undef\, ccdlflags='-rdynamic'   cccdlflags='-fPIC'\, lddlflags='-shared -L/usr/local/lib'

Characteristics of this binary (from libperl)​:   Compile-time options​:   Built under linux   Compiled at Aug 9 2001 22​:48​:52   @​INC​:   /usr/lib/perl5/5.6.0/i386-linux   /usr/lib/perl5/5.6.0   /usr/lib/perl5/site_perl/5.6.0/i386-linux   /usr/lib/perl5/site_perl/5.6.0   /usr/lib/perl5/site_perl   .

p5pRT commented 21 years ago

From @eserte

"aw464@​osfn.org (via RT)" \perlbug\-followup@&#8203;perl\.org writes​:

# New Ticket Created by aw464@​osfn.org # Please include the string​: [perl #19154] # in the subject line of all future correspondence about this issue. # \<URL​: http​://rt.perl.org/rt2/Ticket/Display.html?id=19154 >

I was trying to create a perl script to search through all my fonts directories\, check for various errors\, and print out a small sample of each of my fonts (also I was fantasizing about automatically fixing errors).

In my script\, there is a foreach loop. The foreach loop should run once for each directory name in the list\, with the loop variable set to each directory name in turn. The foreach loop runs the correct number of times\, and the loop variable is usually set to the correct directory name\, but for one run of the foreach loop\, the loop variable is set to undef instead of the correct directory name\, which I think is a bug in perl.

I run the script with standard error redirected to a file. In the middle of the error file\, there are the following nine lines​:

Use of uninitialized value in chdir at ./listfonts line 78. Use of uninitialized value in concatenation (.) at ./listfonts line 79.

errors in directory '' unable to read file fonts.dir unable to read file Fontmap Use of uninitialized value in concatenation (.) at ./listfonts line 135.

errors in directory '/dos/windows/system'

These error messages show that $SelectedDirectory is set to undef. This occurs before $SelectedDirectory is set to '/dos/windows/system'; therefore $SelectedDirectory is set to undef when $SelectedDirectory should be set to '/usr/share/fonts/default/Type1'. I think this is a bug in perl.

I can activate and deactivate the bug by making a small change elsewhere in the script. This is explained in the comments in the script.

I am not asking for help. I am submitting this script because I think it might help you to debug perl.

I do not know if this bug can be duplicated without the exact same directories and files as I have.

perl 5.6.0 is quite old. Can you reproduce the problem with perl 5.6.1 or perl 5.8.0?

Regards\,   Slaven

-- Slaven Rezic - slaven.rezic@​berlin.de

  tktimex - project time manager   http​://sourceforge.net/projects/ptktools/

p5pRT commented 21 years ago

From japhy@pobox.com

On Dec 15\, aw464@​osfn.org (via RT) said​:

#for ( @​FontPath ) { $SelectedDirectory = $_; # if the previous line is not commented and the next line is # commented\, then the bug bites. if the previous line is # commented and the next line is not commented\, the bug does # not bite. Note that the bug does not bite here\, the bug bites below. # Changing these lines seems to activate and deactivate the bug. foreach $SelectedDirectory ( @​FontPath ) { if ( ! chdir($SelectedDirectory) ) { &zNoChDir; next } if ( open(INPUT\,"\< Fontmap") ) { while (\) {

Here is the root of your problem​:

  @​list = (1 .. 5);   for (@​list) {   open FILE\, "foo.$_";   while (\) { ... }   close FILE;   }

Now @​list is (undef\, undef\, undef\, undef\, undef). This is because the construct

  while (\)

is the same as

  while (defined($_ = \))

which means that when FILE runs out\, $_ is set to undef. And since in a loop such as

  for (@​list) { ... }

$_ is an alias to the ACTUAL element in @​list\, so that when $_ is set to undef\, the element in @​list is set to undef too.

-- Jeff "japhy" Pinyan japhy@​pobox.com http​://www.pobox.com/~japhy/ RPI Acacia brother #734 http​://www.perlmonks.org/ http​://www.cpan.org/ \ what does y/// stand for? \ why\, yansliterate of course. [ I'm looking for programming work. If you like my work\, let me know. ]

p5pRT commented 21 years ago

From alex@rcon.org

Not a bug\, as explained in correspondence.

p5pRT commented 21 years ago

alex@rcon.org - Status changed from 'new' to 'resolved'