//
// Written by John A deLaubenfels, https://www.adaptune.com/steg.htm .
// Permission granted to use freely in any non-fraudulent way.
//


#ifdef NEED_PCH
   #include "pch.h"
#else
   #include "stdafx.h"
#endif // !NEED_PCH

#include "ssprint.h"


#ifdef _DEBUG
// #define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif





//
// new 12-02-08 (jdl)
//
//
// This function behaves just like sprintf(), but writes to a std::string rather than a char *.
//
// Example use: 
//    ssprint(ssA, "This is my %d attempt to %8.4f format %s stuff into std::strings.", 1, 36.4218926, "bozo");
//
//
// Return value: number of chars written.
//
//
//
// qxssprint()
//

int ssprint(std::string &ssOut, const char *fmt, va_list &ap) {
    int i;
    short is, cfmt, c, c2;
    long il;
    int lng;
    int done = 0;
    // float fs;
    double f;
    long double fl;
    char *cp;
    void *vp;
    const char *cp_fmt;
    const int clilLen = 256;
    char clil[clilLen];

    // start with each '%' found:
    char *cp_pct;
    static char cpercent[32];

    ssOut.clear();

    for(cp_fmt=fmt; ((cfmt=*(cp_fmt++)) != '\0'); ) {
        if(cfmt == '%') {
            // how much to handle?
            cp_pct = cpercent;
            *(cp_pct++) = (char)cfmt;   // start with '%'...
            lng  = 0;
            done = 0;
            while(!done) {
                switch(c2 = *(cp_fmt++)) {
                    case 'd':
                    case 'o':
                    case 'x':
                    case 'X':
                    case 'u':
                    case 'c':
                        *(cp_pct++) = (char)c2;
                        *(cp_pct++) = '\0';
                        switch(lng) {
                            case -1:
#ifdef BAD_CODE // Linux compiler sez shorts are promoted to int on the stack                            
                                is = va_arg(ap, short);
#ifdef _WIN32                                
                                sprintf_s(clil, clilLen, cpercent, is);
#else
                                sprintf(clil, cpercent, is);
#endif                                
                                break;
#endif // BAD_CODE // Linux compiler sez shorts are promoted to int on thestack                                                            
                            case 0:
                                i = va_arg(ap, int);   // actually, same as short
                                // sprintf_s(clil, clilLen, cpercent, i);
#ifdef _WIN32                                
                                sprintf_s(clil, clilLen, cpercent, i);
#else
                                sprintf(clil, cpercent, i);
#endif                                
                                break;
                            case 1:
                            case 2:
                                il = va_arg(ap, long);
                                // sprintf_s(clil, clilLen, cpercent, il);
#ifdef _WIN32                                
                                sprintf_s(clil, clilLen, cpercent, il);
#else
                                sprintf(clil, cpercent, il);
#endif                                
                                break;
                        }
                        done = 1;    // a terminating char...
                        break;
                    case 'f':
                    case 'e':
                    case 'E':
                    case 'g':
                    case 'G':
                        *(cp_pct++) = (char)c2;
                        *(cp_pct++) = '\0';
                        switch(lng) {
                            case -1:
#ifdef BAD_CODE  // floats promoted to double                            
                                fs = va_arg(ap, float);
                                // sprintf_s(clil, clilLen, cpercent, (double)fs);  // double is default for c...
#ifdef _WIN32                                
                                sprintf_s(clil, clilLen, cpercent, (double)fs);  // double is default for c...
#else
                                sprintf(clil, cpercent, (double)fs);  // double is default for c...
#endif                                
                                break;
#endif // BAD_CODE                                
                            case 0:
                            case 1:    // see p. 2-105: %f same as %lf
                                f = va_arg(ap, double);
                                // sprintf_s(clil, clilLen, cpercent, f);
#ifdef _WIN32                                
                                sprintf_s(clil, clilLen, cpercent, f);
#else
                                sprintf(clil, cpercent, f);
#endif                                
                                break;
                            case 2:
                                fl = va_arg(ap, long double); // not FORTRAN-compatible...
                                // sprintf_s(clil, clilLen, cpercent, fl);
#ifdef _WIN32                                
                                sprintf_s(clil, clilLen, cpercent, fl);
#else
                                sprintf(clil, cpercent, fl);
#endif                                
                                break;
                        }
                        done = 1;    // a terminating char...
                        break;
                    case 'h':
                        *(cp_pct++) = (char)c2;
                        lng = -1;    // short...
                        break;
                    case 'l':
                        *(cp_pct++) = (char)c2;
                        lng = 1;     // long...
                        break;
                    case 'L':
                        *(cp_pct++) = (char)c2;
                        lng = 2;     // really long...
                        break;
                    case 'p':
                        *(cp_pct++) = (char)c2;
                        *(cp_pct++) = '\0';
                        vp = va_arg(ap, void *);
                        // sprintf_s(clil, clilLen, cpercent, vp);
#ifdef _WIN32                                
                        sprintf_s(clil, clilLen, cpercent, vp);
#else
                        sprintf(clil, cpercent, vp);
#endif                                
                        done = 1;    // a terminating char... 
                        break;
                    case 's':
                        *(cp_pct++) = (char)c2;
                        *(cp_pct++) = '\0';
                        cp = va_arg(ap, char *);
                        // sprintf_s(clil, clilLen, cpercent, cp);
#ifdef _WIN32                                
                        sprintf_s(clil, clilLen, cpercent, cp);
#else
                        sprintf(clil, cpercent, cp);
#endif                                
                        done = 1;    // a terminating char...
                        break;
                    case '*':       // pop number:
#ifdef _WIN32                                
                        is = va_arg(ap, short);                      // qxfix??  Check on Windows.
                        // sprintf_s(clil, clilLen, "%d", is);
                        sprintf_s(clil, clilLen, "%d", is);
#else
                        i = va_arg(ap, int);
                        sprintf(clil, "%d", i);
#endif                                
                        // copy the expanded number to the percent string:
                        for(cp=clil; ((c=*cp) != '\0'); cp++) {
                            *(cp_pct++) = (char)c;
                        }
                        break;
                    case '%':       // sequence "%%" -> "%"
                        // sprintf_s(clil, clilLen, "%c", c2);
#ifdef _WIN32                                
                        sprintf_s(clil, clilLen, "%c", c2);
#else
                        sprintf(clil, "%c", c2);
#endif                                
                        done = 1;    // a terminating char...
                        break;
                    default:
                        *(cp_pct++) = (char)c2;
                        break;
                }  // switch(c2 = *(cp_fmt++))
            }     // while(!done) [with this % format stuff...]

            // once here, we're done evaluating %.. string; clil has stuff...
            ssOut += clil;
        } else {
            // fprintf(stdout, "Format char unimportant: %c.\n", cfmt);
            ssOut += (char)cfmt;
        }   // (NOT) if(cfmt == '%')
    }      // for(cp_fmt=fmt; ((cfmt=*(cp_fmt++) != '\0'); )

    return (int)ssOut.size();

} // end of ssprint()...




// qxssprint();

int ssprint(std::string &ssOut, const char *fmt, ...) {
    int iRet = 0;

    va_list ap;               // see K&R p. 156.
    va_start(ap, fmt);

    iRet = ssprint(ssOut, fmt, ap);

    va_end(ap);

    return iRet;
} // end of ssprint()...




// qxssAppend()

int ssAppend(std::string &ssOut, const char *fmt, ...) {
    int iRet = 0;
    std::string ssLoc;

    va_list ap;               // see K&R p. 156.
    va_start(ap, fmt);

    iRet = ssprint(ssLoc, fmt, ap);

    va_end(ap);

    ssOut += ssLoc;

    return iRet;
} // end of ssappend()...





