scanf/vsscanf.c

Go to the documentation of this file.
00001 // Copyright 2006 Benedikt Böhm <hollow@gentoo.org>
00002 //
00003 // This program is free software; you can redistribute it and/or modify
00004 // it under the terms of the GNU General Public License as published by
00005 // the Free Software Foundation; either version 2 of the License, or
00006 // (at your option) any later version.
00007 //
00008 // This program is distributed in the hope that it will be useful,
00009 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 // GNU General Public License for more details.
00012 //
00013 // You should have received a copy of the GNU General Public License
00014 // along with this program; if not, write to the
00015 // Free Software Foundation, Inc.,
00016 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 
00018 #include "scanf.h"
00019 #include "str.h"
00020 
00021 enum __scanf_flags {
00022         SFL_NOOP  = 0x01,
00023         SFL_WIDTH = 0x02,
00024 };
00025 
00026 enum __scanf_rank {
00027         SFR_CHAR,
00028         SFR_SHORT,
00029         SFR_INT,
00030         SFR_LONG,
00031         SFR_LLONG,
00032 };
00033 
00034 #define SFR_MIN SFR_CHAR
00035 #define SFR_MAX SFR_LLONG
00036 
00037 enum __scanf_state {
00038         SFS_NORMAL,
00039         SFS_FLAGS,
00040         SFS_WIDTH,
00041         SFS_MOD,
00042         SFS_CONV,
00043         SFS_STORE,
00044         SFS_EOF,
00045         SFS_ERR,
00046 };
00047 
00048 typedef struct {
00049         int f; /* flags */
00050         int l; /* length */
00051         int s; /* state */
00052         int w; /* width */
00053 } __scanf_t;
00054 
00055 /* supported formats:
00056 ** - field width
00057 ** - length mods: hh, h, l, and ll
00058 ** - conversion spec: d, i, u, o, x, X, c, s, p, P, n */
00059 int _lucid_vsscanf(const char *str, const char *fmt, va_list _ap)
00060 {
00061         /* keep track of converted arguments */
00062         int converted = 0;
00063         
00064         /* current character in format */
00065         char c;
00066         
00067         /* current conversion data */
00068         __scanf_t f;
00069         
00070         /* arguments */
00071         union {
00072                 /* unsigned argument */
00073                 unsigned long long int u;
00074                 
00075                 /* string argument */
00076                 char *s;
00077         } arg;
00078         
00079         /* base used for integer conversions */
00080         int base;
00081         
00082         /* number of bytes converted in str_toumax */
00083         int len;
00084         
00085         /* pointer for string conversion */
00086         char *sp;
00087         
00088         /* don't consume original ap */
00089         va_list ap;
00090         va_copy(ap, _ap);
00091         
00092         /* initialize conversion data */
00093         f.f = 0;
00094         f.l = SFR_INT;
00095         f.s = SFS_NORMAL;
00096         f.w = str_len(str);
00097         
00098         while ((c = *fmt++)) {
00099                 switch (f.s) {
00100                 case SFS_NORMAL:
00101                         if (c == '%') {
00102                                 f.f = 0;
00103                                 f.l = SFR_INT;
00104                                 f.s = SFS_FLAGS;
00105                                 f.w = str_len(str);
00106                         }
00107                         
00108                         else if (char_isspace(c))
00109                                 while (char_isspace(*str))
00110                                         str++;
00111                         
00112                         else if (*str == c)
00113                                 str++;
00114                         
00115                         else
00116                                 f.s = SFS_ERR;
00117                         
00118                         break;
00119                 
00120                 case SFS_FLAGS:
00121                         switch (c) {
00122                         case '*':
00123                                 f.f |= SFL_NOOP;
00124                                 break;
00125                         
00126                         case '0':
00127                         case '1':
00128                         case '2':
00129                         case '3':
00130                         case '4':
00131                         case '5':
00132                         case '6':
00133                         case '7':
00134                         case '8':
00135                         case '9':
00136                                 f.w  = (c - '0');
00137                                 f.f |= SFL_WIDTH;
00138                                 f.s  = SFS_WIDTH;
00139                                 break;
00140                         
00141                         default:
00142                                 f.s = SFS_MOD;
00143                                 fmt--;
00144                                 break;
00145                         }
00146                         
00147                         break;
00148                 
00149                 case SFS_WIDTH:
00150                         if (c >= '0' && c <= '9')
00151                                 f.w = f.w * 10  + (c - '0');
00152                         
00153                         else {
00154                                 f.s = SFS_MOD;
00155                                 fmt--;
00156                         }
00157                         
00158                         break;
00159                 
00160                 case SFS_MOD:
00161                         switch (c) {
00162                         case 'h':
00163                                 f.l--;
00164                                 break;
00165                         
00166                         case 'l':
00167                                 f.l++;
00168                                 break;
00169                         
00170                         default:
00171                                 f.s = SFS_CONV;
00172                                 fmt--;
00173                                 break;
00174                         }
00175                         
00176                         break;
00177                 
00178                 case SFS_CONV:
00179                         f.s = SFS_NORMAL;
00180                         
00181                         if (f.l > SFR_MAX)
00182                                 f.l = SFR_MAX;
00183                         
00184                         if (f.l < SFR_MIN)
00185                                 f.l = SFR_MIN;
00186                         
00187                         switch (c) {
00188                         case 'd':
00189                                 base = 10;
00190                                 goto scan_int;
00191                         
00192                         case 'i': /* signed conversion */
00193                                 base = 0;
00194                                 goto scan_int;
00195                         
00196                         case 'o':
00197                                 base = 8;
00198                                 goto scan_int;
00199                         
00200                         case 'u':
00201                                 base = 10;
00202                                 goto scan_int;
00203                         
00204                         case 'X':
00205                         case 'x':
00206                                 base = 16;
00207                                 goto scan_int;
00208                         
00209                         scan_int:
00210                                 while (char_isspace(*str))
00211                                         str++;
00212                                 
00213                                 if (!*str) {
00214                                         f.s = SFS_EOF;
00215                                         break;
00216                                 }
00217                                 
00218                                 len = str_toumax(str, &arg.u, base, f.w);
00219                                 
00220                                 if (len <= 0) {
00221                                         f.s = SFS_ERR;
00222                                         break;
00223                                 }
00224                                 
00225                                 str += len;
00226                                 converted++;
00227                                 
00228                                 if (!(f.f & SFL_NOOP)) {
00229                                         switch (f.l) {
00230                                         case SFR_CHAR:
00231                                                 *va_arg(ap, unsigned char *) = arg.u;
00232                                                 break;
00233                                         
00234                                         case SFR_SHORT:
00235                                                 *va_arg(ap, unsigned short int *) = arg.u;
00236                                                 break;
00237                                         
00238                                         case SFR_INT:
00239                                                 *va_arg(ap, unsigned int *) = arg.u;
00240                                                 break;
00241                                         
00242                                         case SFR_LONG:
00243                                                 *va_arg(ap, unsigned long int *) = arg.u;
00244                                                 break;
00245                                         
00246                                         case SFR_LLONG:
00247                                                 *va_arg(ap, unsigned long long int *) = arg.u;
00248                                                 break;
00249                                         
00250                                         default:
00251                                                 *va_arg(ap, unsigned long long int *) = arg.u;
00252                                                 break;
00253                                         }
00254                                 }
00255                                 
00256                                 break;
00257                         
00258                         case 'c': /* character conversion */
00259                                 /* default width = 1 */
00260                                 f.w = (f.f & SFL_WIDTH) ? f.w : 1;
00261                                 
00262                                 if ((f.f & SFL_NOOP)) {
00263                                         while (f.w--) {
00264                                                 if (!*str) {
00265                                                         f.s = SFS_EOF;
00266                                                         break;
00267                                                 }
00268                                                 
00269                                                 str++;
00270                                         }
00271                                 }
00272                                 
00273                                 else {
00274                                         arg.s = va_arg(ap, char *);
00275                                         
00276                                         while (f.w--) {
00277                                                 if (!*str) {
00278                                                         f.s = SFS_EOF;
00279                                                         break;
00280                                                 }
00281                                                 
00282                                                 *arg.s++ = *str++;
00283                                         }
00284                                 }
00285                                 
00286                                 if (f.s != SFS_EOF && !(f.f & SFL_NOOP))
00287                                         converted++;
00288                                 
00289                                 break;
00290                         
00291                         case 's': /* string conversion */
00292                                 if (!(f.f & SFL_NOOP)) {
00293                                         while (f.w-- && !char_isspace(*str)) {
00294                                                 if (!*str) {
00295                                                         f.s = SFS_EOF;
00296                                                         break;
00297                                                 }
00298                                                 
00299                                                 str++;
00300                                         }
00301                                 }
00302                                 
00303                                 else {
00304                                         sp = arg.s = va_arg(ap, char *);
00305                                         
00306                                         while (f.w-- && !char_isspace(*str)) {
00307                                                 if (!*str) {
00308                                                         f.s = SFS_EOF;
00309                                                         break;
00310                                                 }
00311                                                 
00312                                                 *sp++ = *str++;
00313                                         }
00314                                         
00315                                         if (f.s != SFS_EOF)
00316                                                 *sp = '\0';
00317                                 }
00318                                 
00319                                 if (f.s != SFS_EOF && !(f.f & SFL_NOOP))
00320                                         converted++;
00321                                 
00322                                 break;
00323                         
00324                         case 'P':
00325                         case 'p': /* pointer conversion */
00326                                 while (char_isspace(*str))
00327                                         str++;
00328                                 
00329                                 if (!*str) {
00330                                         f.s = SFS_EOF;
00331                                         break;
00332                                 }
00333                                 
00334                                 len = str_toumax(str, &arg.u, 0, f.w);
00335                                 
00336                                 if (len <= 0) {
00337                                         f.s = SFS_ERR;
00338                                         break;
00339                                 }
00340                                 
00341                                 if (!(f.f & SFL_NOOP))
00342                                         *va_arg(ap, void **) = (void *) (unsigned long int) arg.u;
00343                                 
00344                                 str += len;
00345                                 converted++;
00346                                 
00347                                 break;
00348                         
00349                         case 'n':
00350                                 *va_arg(ap, int *) = converted;
00351                                 
00352                                 break;
00353                         
00354                         case '%':
00355                                 if (*str == '%')
00356                                         str++;
00357                                 else
00358                                         f.s = SFS_ERR;
00359                                 
00360                                 break;
00361                         
00362                         default:
00363                                 f.s = SFS_ERR;
00364                                 break;
00365                         }
00366                         
00367                         break;
00368                         
00369                 case SFS_EOF:
00370                         converted = converted ? converted : -1;
00371                         
00372                 case SFS_ERR:
00373                         va_end(ap);
00374                         return converted;
00375                 }
00376         }
00377         
00378         if (f.s == SFS_EOF)
00379                 converted = converted ? converted : -1;
00380         
00381         va_end(ap);
00382         return converted;
00383 }

Generated on Sun Dec 3 17:45:53 2006 for lucid by  doxygen 1.5.1