/*
 *  pwloginstr.c	v1.1		
 *
 *  loginstr() parsing
 *
 *  Copyright (C), 1994	Riku Meskanen - mesrik@tukki.jyu.fi
 */

/*
This file is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This file is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

I you don't have a copy of the GNU General Public License
write to the Free Software Foundation, 675 Mass Ave, 
Cambridge, MA 02139, USA.  
*/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

static void
warning(s)
char *s;
{
     fprintf(stderr,"Warning: /etc/login.defs LOGIN_STRING %s.\n", s);
}

static	char	*LOGIN_DEFAULT = "Password :";
static  char    *too_long      = "too long";
static  char    *bad_escape    = "syntax error, bad escape";
static  char    *null_string   = "syntax error, parsed string null";
static  char    *quote_missing = "syntax error, quote-mark missing";

char
*pwloginstr(trgt, tmax, fmt, user)
char *trgt;
size_t tmax;
char *fmt;
char *user;
{
    char  *rp;	/* parse read pointer */
    char  *tp;	/* target pointer */
    short  ql;	/* quote level */
    size_t ts;  /* target size */
    size_t tmp; /* temp sizer */

    tp = trgt;
    *tp= '\0';
    ql = 0;
    ts = 0;

    while (*fmt && ql != -1) {
        switch (*fmt) {
            case '"' : 	if (!ql) {  /* quote */
			    ++ql;	/* enter in quoted string */
                            ++fmt;
			    continue;
                        }
			ql = -1;	/* exit from quoted string */
			break;

            case '\\' : if (!ql) {
                           warning(quote_missing);
                           return LOGIN_DEFAULT;
                        }

                        if (ts +2 > tmax) {     /* char + nul */
                           warning(too_long);
                           return LOGIN_DEFAULT;
                        }

                        rp = NULL;
            		switch (*(fmt +1)) {
                                 case 'a' : *tp = '\a';  break;      /* BEL */
                                 case 'b' : *tp = '\b';  break;      /* BS  */
                                 case 'f' : *tp = '\f';  break;      /* FF  */
                                 case 'n' : *tp = '\n';  break;      /* CR  */
                                 case 'r' : *tp = '\r';  break;      /* LF  */
                                 case 't' : *tp = '\t';  break;      /* HT  */
                                 case 'v' : *tp = '\v';  break;      /* VT  */
                                 case '\\': *tp = '\\';  break;      /* backspace */
                                 case '?' : *tp = '\?';  break;      /* question mark */
                                 case '\'': *tp = '\'';  break;      /* single quote  */
                                 case '\"': *tp = '\"';  break;      /* double quote  */

                                 /* check if it's valid hex, octal or decimal
                                  * syntax: 0     prefixed octals            ( base  8 )
                                  *         0x    "        hexadesimals      ( base 16 )
                                  *         [1-9] "        decimal           ( base 10 )
                                  */
                                 default  : if (isdigit((int) *(fmt+1))) {
                                                /* yeah i know -- this sure is BFMI code. We lose
						 * some significant bits here, i don't care ;-)
						 */
                                                *tp = (char) strtol((const char *) (fmt+1), &rp, 0);
						
                                                if (tp < rp)   /* did we get any bytes? */
                                                    break;     /* yes, we did, so leave the case */
					    }
					    warning(bad_escape);  
					    return LOGIN_DEFAULT;
		        }

		        if (rp == NULL)		/* did we visit strtol() ? */ 
		           fmt += 2;		/* no, skip over C std escape */
                        else
                           fmt += rp - fmt;	/* yes, skip over octal etc... */

                        ++tp;			/* adjust target and it's size */
                        ++ts;
			break;

	    case '%' :  if (!ql) {
                            warning(quote_missing);
                            return LOGIN_DEFAULT;
                        }
                        switch (*(fmt +1)) {
						/* %u to username */
				case 'u':       if (ts + (tmp = strlen(user)) > tmax) {
                                                   warning(too_long);
                                                   return LOGIN_DEFAULT;
                                                }
                                                strcat(tp,user);
						tp += tmp;	/* adjust target pointer */
                                                ts += tmp;     /* and size */
                                                tmp = 0;       /* safe, if used later elsewere */
						++fmt;
						break;
						/* anything else is % itself */
				default:	if (ts +2 > tmax) {
						   warning(too_long);
						   return LOGIN_DEFAULT;	
						}
						*tp++ = '%';
						++ts;
						break;
			}
			++fmt;
			break;

	    default   : if (!ql) {  /* if not in quoted string */
				while (*fmt && *fmt != '"')	/* get first quote */
                              		++fmt;
				break;
			}
			*tp = *fmt;
			++fmt;
			++tp;
                        ++ts;
        }	/* switch */

	*tp = '\0';

    }	/* while */

    if (ql > 0 ) {
       warning(quote_missing);
       return LOGIN_DEFAULT;
    }


    if (ts == 0) {	/* if target is empty */
        warning(null_string);
        return LOGIN_DEFAULT;
    }

    return trgt;
}


#ifdef __TEST__
/*
 *   simple test
 */

char *
getstring(msg, str, mlen)
char *msg;
char *str;
int  mlen;
{
	char *s;

        printf("%s ", msg);
	if ((s = fgets(str, mlen, stdin)) != NULL && *s != '\n') {
	    *(s + strlen(s) -1) = '\0';	/* remove newline */
	    return s;
	}
        else
            return NULL;
}

int
main()
{
    char ls[80];
    char fmt[60];
    char usr[32];
    
    while (getstring("LOGIN_STRING: ", fmt, sizeof(fmt)) &&
           getstring("USER: ", usr, sizeof(usr))) {
         printf("PROMPT: %s\n", pwloginstr(ls, sizeof(ls), fmt, usr) );
    }

    return 0;
}

#endif
/* eof pwloginstr.c */
