[EMAIL PROTECTED] writes:

> Yeah, but WHY?  Obviously Greg had too much time on his hands...

Because it was there.

Anyways, here's my contribution to this type of zaniness.

--kevin

PS  I wrote this because I *didn't* have enough time on my hands.




#!/usr/bin/perl

# Author: Kevin D. Clark ([EMAIL PROTECTED])

# Description:  replaces old C-style sprintf() code with newer C++ 
#               string-stream code (this greatly increases type safety)

# Motivation: A colleague of mine left a body of code for me to maintain.
#             This body consisted of tens of thousands of lines of code.
#             Unfortunately, she assumed that "all the world's a 32-bit SPARC",
#             and when we had to port this code to other architectures all
#             of her calls to sprintf() (over 1500 in all) blew up.  Faced with
#             the tedious task of fixing this code, I wrote this.

# Warning: Caveat Programmer!


# Assumptions and Shortcomings:  
# I assume that people are using sprintf() in a legal way.
# Hopefully not too many people do stuff like:  foo(a, b, (c, d), e);
# Bug:  Hopefully not too many people do stuff like:  foo(a, b, ",c", d);
#       (I don't think that this is very common in the case of sprintf())


# Design problem:  I don't see a great way to get string-streams to 
# handle things like "%.5s" and "%g".  (sigh)
# (4/10/97)  Actually, now I do see how to handle things like %.5s".
#            As soon as I get un-busy, I plan on adding support for this.



# I could hack this to handle non-pathological cases in which comments were
# embedded in the actual call to sprintf(), but this would make this much more
# complex and I don't think that there enough cases of this to warrent the 
# additional work necessary.
# Bug:  If the call to sprintf() is commented out like:
#       // sprintf(...) things tend to die quickly.

# Bug:  if you do something like this, you're screwed:
#
#    sprintf(str, (newVal > 1.0) ? "%.1lf" : "%.4lf", newVal);
#
# I don't plan on fixing this one.... (-:

# Bug: the size of the character array is static.



# TODO:  \n -> endl
#
#        warn about the bogus %lf conversion
#
#        make the trailing semicolon disappear
#
#        warn about long doubles -- the HP C++ compiler comes with an
#        ostream class that doesn't handle this type (duh!)
#
#        make this program handle fprintf() and printf() too.
#
#        convert this to use the new C++ Standard Library iostreams


#######################################################################
# Example:
#
# (this example is a wee-bit pathological)
#
# $ cat foo.cc
# 
# #include <iostream.h>
# #include <stdio.h>
# 
# int main() 
# {
#   char buf[100];
#   double foo=1;
#   int bar=2; 
# 
#   sprintf(buf, 
# 
#           "%-2.f\"%%\n%d",
# 
#           foo, 
# 
#           bar);
# 
# 
#   cout << buf << endl;
# }
# $ replace-sprintf.pl < foo.cc
# 
# #include <iostream.h>
# #include <stdio.h>
# #include <iostream.h>
# #include <iomanip.h>
# #include <strstream.h>
# #include <string.h>
# 
# int main() 
# {
#   char buf[100];
#   double foo=1;
#   int bar=2;
#    { // replacement for sprintf() code
#      char tempBuf[256];
#      ostrstream tempStream(tempBuf, sizeof tempBuf);
#      tempStream.setf(ios::left,ios::adjustfield);
#      tempStream << setw(2)
#                  << setprecision(0)
#                  << (double) foo
#                  << "\"";
#      tempStream.setf(ios::right,ios::adjustfield);
#      tempStream << setw(0)
#                  << "%\n"
#                  <<  bar
#                  << ends;
#      strcpy(buf, tempBuf);
#    };
#  
#  
#   cout << buf << endl;
# }
# $  
#######################################################################

# Last caveat:  I wrote this on a bet and I wrote it rather quickly....

require 5;

$tempBufSize=256;
$w = ' ' x length(q(tempStream <<));

$replace=q#
  my $whole = $&;
  my $cannotDo = 0;
  my $space;
  ($space=$1) =~ y/\012//d;
  my $to = $2;
  my $rest = $5;
  my $format, $result;
  my $ignoreFirst = 0;
  ($format = "$3$4") =~ s/"$//;
  $result = "${space}tempStream";





  if (   $format =~ /%-?\d+\.?s/
      || $format =~ /%-?\.?\d+s/
      || $format =~ /%-?\d+\.?\d+s/) {
    $cannotDo = 1;
    warn "$ARGV: warning: unable to handle a complex sprintf() (no change made)\n";
  }





  if (! $format =~ /%/) {
    $result .= $format;
  }
  else {
    $index = 0;
    $bare = 1;
    $format =~ s/%%/%@/g;
    if (! ($format =~ /^%/)) {
      $ignoreFirst = 1;    
    }
    @f = split(/%/, $format);
    $rest =~ y/\012//d;
    @rest = split(/,/, $rest);
    foreach $spec (@f) {
      if ($ignoreFirst) {
        $ignoreFirst = 0;
        $result .= " << \"$spec\"\n${space}${w}";
      }
      elsif ($spec =~ /^@/) {
          $spec =~ s/^.//;
          $result .= " << \"%$spec\"\n${space}${w}";
          $index--;
      }
      elsif ($spec =~ /^-/) {
         $spec =~ s/^.//;
         $leftJust = 1;
         if (!$bare) {
           $result .= ";\n${space}${w}tempStream";
         }
         $result .= ".setf(ios::left,ios::adjustfield);\n${space}  tempStream";
         redo;
       }
       elsif ($spec =~ /^\+/) {
         $spec =~ s/^.//;

       }
       elsif ($spec =~ /^0/) {
         $spec =~ s/^.//;
         $zeroPad = 1;
         $bare = 0;
         $result .= " << setfill('0')\n${space}${w}";
         redo;
       }
       elsif ($spec =~ /^\#/) {
         $spec =~ s/^.//;

       }
       elsif ($spec =~ /^\ /) {
         $spec =~ s/^.//;

       }
       elsif ($spec =~ /^([1-9]+)/) {
         $width = $1;
         $bare = 0;
         $result .= " << setw($width)\n${space}${w}";
         $spec =~ s/^[1-9]+//;
         redo;
       }
       elsif ($spec =~ /^\.(\d*)/) {
         $bare = 0;
         if ($1) {
           $precisionFlag = 1;
           $precision = $1;
         }
         else {
           $precision = 0;
         }
         $result .= " << setprecision($precision)\n${space}${w}";
         $spec =~ s/^\.[1-9]*//;
         redo;
       }
       elsif ($spec =~ /^l/i) {
         $sizeSpec = "(long)";
         $spec =~ s/^l//i;
         redo;
       }
       elsif ($spec =~ /^h/) {
         $sizeSpec = "(short)";
         $spec =~ s/^h//;
         redo;
       }
       elsif ($spec =~ /^[di]/) {
         $spec =~ s/^.//;
         $bare = 0;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         $result .= " << $sizeSpec$rest[$index]\n${space}${w}";
         $result .= " << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^u/) {
         $spec =~ s/^.//;
         $bare = 0;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec) {
           $sizeSpec =~ s/\(/\(unsigned /;
         }
         else {
           $sizeSpec = "(unsigned)";
         }
         $result .= " << $sizeSpec$rest[$index]\n${space}${w}";
         $result .= " << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^o/) {
         $spec =~ s/^.//;
         $bare = 0;
         $result .= "<< setfill('0')";
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec) {
           $sizeSpec =~ s/\(/\(unsigned /;
         }
         else {
           $sizeSpec = "(unsigned)";
         }
         $result .= " << oct  << $sizeSpec$rest[$index]  << dec\n${space}${w}";
         $result .= " << setfill(' ') << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^x/) {
         $spec =~ s/^.//;
         $bare = 0;
         $result .= "<< setfill('0')";
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec) {
           $sizeSpec =~ s/\(/\(unsigned /;
         }
         else {
           $sizeSpec = "(unsigned)";
         }
         $result .= " << hex  << $sizeSpec$rest[$index]  << dec\n${space}${w}";
         $result .= "<< setfill(' ') << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^X/) {
         print STDERR "Warning:  $ARGV: %X not supported correctly!\n";
         $spec =~ s/^.//;
         $bare = 0;
         $result .= "<< setfill('0')";
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec) {
           $sizeSpec =~ s/\(/\(unsigned /;
         }
         else {
           $sizeSpec = "(unsigned)";
         }
         $result .= " << hex  << $sizeSpec$rest[$index]  << dec\n${space}${w}";
         $result .= "<< setfill(' ') << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^c/) {
         $spec =~ s/^.//;
         $bare = 0;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         $result .= " << (unsigned char)$rest[$index]\n${space}${w}";
         $result .= " << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^s/) {
         $spec =~ s/^.//;
         $bare = 0;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         $result .= " << (const char *)$rest[$index]\n${space}${w}";
         $result .= " << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^p/) {
         $spec =~ s/^.//;
         $bare = 0;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         $result .= " << (void *)$rest[$index]\n${space}${w}";
         $result .= " << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^n/) {
         $spec =~ s/^.//;
         $bare = 0;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec) {
           $sizeSpec =~ s/\)/ *)/;
         }
         else {
           $sizeSpec = "(int *)";
         }
         $result .= " << $sizeSpec$rest[$index]\n${space}${w}";
         $result .= " << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^f/) {
         $spec =~ s/^.//;
         $bare = 0;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec eq "(long)") {
           $sizeSpec = "(long double)";
         }
         elsif ($sizeSpec eq "") {
           $sizeSpec = "(double)";
         }
         else {
           print STDERR 
                 "WARNING: $ARGV: there is no such thing as a short double (%hf)!\n";
           $result .=
              "WARNING: there is no such thing as a short double (%hf)!\n";
         }
         $result .= " << $sizeSpec$rest[$index]\n${space}${w}";
         $result .= " << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^e/) {
         $spec =~ s/^.//;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec eq "(long)") {
           $sizeSpec = "(long double)";
         }
         elsif ($sizeSpec eq "") {
           $sizeSpec = "(double)";
         }
         else {
           print STDERR
                 "WARNING: $ARGV: there is no such thing as a short double (%hf)!\n";
           $result .=
              "WARNING: there is no such thing as a short double (%hf)!\n";
         }
         if (!$bare) {
           $result .= ";\n${space}  tempStream";
         }
         $result .= ".setf(ios::scientific,ios::floatfield);\n${space}";
         $result .= "  tempStream << $sizeSpec$rest[$index];\n${space}";
         $result .= "  tempStream.setf(ios::fixed,ios::floatfield);\n${space}";
         $result .= "  tempStream << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^E/) {
         print STDERR "$ARGV: warning: %E conversion not handled correctly!\n";
         $spec =~ s/^.//;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec eq "(long)") {
           $sizeSpec = "(long double)";
         }
         elsif ($sizeSpec eq "") {
           $sizeSpec = "(double)";
         }
         else {
           print STDERR
                 "WARNING: $ARGV: there is no such thing as a short double (%hf)!\n";
           $result .=
              "WARNING: there is no such thing as a short double (%hf)!\n";
         }
         if (!$bare) {
           $result .= ";\n${space}  tempStream";
         }
         $result .= ".setf(ios::scientific,ios::floatfield);\n${space}";
         $result .= "  tempStream << $sizeSpec$rest[$index];\n${space}";
         $result .= "  tempStream.setf(ios::fixed,ios::floatfield);\n${space}";
         $result .= "  tempStream << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }
       elsif ($spec =~ /^g/i) {
         print STDERR "$ARGV: warning: %g conversion not handled correctly!\n";
         $spec =~ s/^.//;
         if (!($rest[$index] =~ /\"/)) {
           $rest[$index] =~ s/\s+/\ /g;
         }
         if ($sizeSpec eq "(long)") {
           $sizeSpec = "(long double)";
         }
         elsif ($sizeSpec eq "") {
           $sizeSpec = "(double)";
         }
         else {
           print STDERR
                 "WARNING: $ARGV: there is no such thing as a short double (%hf)!\n";
           $result .=
              "WARNING: there is no such thing as a short double (%hf)!\n";
         }
         if (!$bare) {
           $result .= ";\n${space}  tempStream";
         }
         $result .= ".setf(ios::scientific,ios::floatfield);\n${space}";
         $result .= "  tempStream << $sizeSpec$rest[$index];\n${space}";
         $result .= "  tempStream.setf(ios::fixed,ios::floatfield);\n${space}";
         $result .= "  tempStream << \"$spec\"\n${space}${w}";
         $cleanup = 1;
       }

       else {
         $result .= $spec;
       }

       if ($cleanup == 1) {
         $cleanup = 0;
         if ($leftJust == 1) {
           $leftJust = 0;
           $result .= ";\n${space}  
tempStream.setf(ios::right,ios::adjustfield);\n${space}  tempStream";
         }
         if ($zeroPad == 1) {
           $zeroPad = 0;
           $result .= " << setfill(' ')\n${space}${w}";
         }
         if ($width) {
           $width = 0;
           $result .= " << setw(0)\n${space}${w}";
         }
       }
       $sizeSpec = "";
       $index++;
    }
  }
  $result =~ s/\s*<< ""\n/\n/gs;
  $result =~ s/<<\s*""//gs;
  if ($cannotDo) {
    "$whole;";
  }
  else {
  $result .= " << ends;
  ${space}strcpy(${to}, tempBuf);";
  "
${space}\{ // replacement for sprintf() code
${space}  char tempBuf[$tempBufSize];
${space}  ostrstream tempStream(tempBuf, sizeof tempBuf);
${space}  tempStream.setf(ios::fixed,ios::floatfield); // reasonable default?
  $result
${space}\}";
}
#;

$SIG{'INT'}='sigHandler';
$SIG{'QUIT'}='sigHandler';
$/=";";
$^I = ($#ARGV == $[-1) ? "" : ".bak"; 
while (<>) {

#  work on this
#  $numQuotes = 0;
#  $numQuotes++ while (/"/g);
#  while ($numQuotes % 2) {  # not perfect
#    $_ .= <>;
#    $numQuotes++ while (/"/g);
#    if (eof) {
#      print $_, "\n";
#      warn "$0: $ARGV: complex sprintf() case encountered.  No changes made\n";
#    }
#  }

  chop;

  $addSemiColon = 1;
  s{(\s*)\b                              # for indentation
    sprintf\s*
    \(([\\[\]\w()*?|~&+\-%.<=>\s]+?),    # handle odd but legal cases
    \s*"([\000-\377]*?)([^\\\\]\")       # be careful
    ([\[\],\w()*&+\-%.>\s]*)\)       # assumed to be legal sprintf() code
   }
   {
     qq($replace)
   }eegsx || ($addSemiColon = 1);
  s{(.*)\n(\s*);}{$1\;}g;
  $_ .= ";" if ($addSemiColon);
  $all .= $_;

  if (eof) {
    push(@lines, split(/\n/, $all));
    $line = 0;
    $lastInclude = 0;
    grep { ++$line; $lastInclude = $line if /^#include/; } @lines;
    $lines[$#lines] =~ s/;$//;
    foreach $line (@lines) {
      if (!$lastInclude--) {
        print "#include <iostream.h>\n";
        print "#include <iomanip.h>\n";
        print "#include <strstream.h>\n";
        print "#include <string.h>\n";
     }
      print "${line}\n";
    }
    undef @lines;
    if ($gotSig) {
      close(ARGV);
      exit 1;
    }
    undef $all;
  }
}

sub sigHandler {
  my($sig) = @_;
  exit(1) if !$^I;
  print STDERR "Caught signal SIG$sig -- cleaning up gracefully...\n";
  $gotSig=1;
}

# Random Zippy quote:
#
# ANN JILLIAN'S HAIR makes LONI ANDERSON'S HAIR look like
# RICARDO MONTALBAN'S HAIR!


__END__



**********************************************************
To unsubscribe from this list, send mail to
[EMAIL PROTECTED] with the following text in the
*body* (*not* the subject line) of the letter:
unsubscribe gnhlug
**********************************************************

Reply via email to