/* $Id: parseopt.C,v 1.10 2001/01/13 19:46:10 dm Exp $ */ /* * * Copyright (C) 1998 David Mazieres (dm@uun.org) * * This program 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 program 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "amisc.h" #include "parseopt.h" char *parseargs::errorbuf = ""; static inline int isspc (char c) { return c == ' ' || c == '\t' || c == '\n'; } static inline int isspcnnl (char c) { return c == ' ' || c == '\t'; } void parseargs::skipblanks () { bool bol = true; while (p < lim) { if (bol && *p == '#') { while (p < lim && *p != '\n') p++; if (p < lim) { lineno++; p++; } continue; } if (isspcnnl (*p)) { bol = false; p++; } else if (*p == '\n') { lineno++; p++; bol = true; } else if (p[0] == '\\' && p[1] == '\n') { p += 2; lineno++; bol = false; } else return; } } void parseargs::skiplwsp () { for (;;) { if (isspcnnl (*p)) p++; else if (p[0] == '\\' && p[1] == '\n') { p += 2; lineno++; } else return; } } str parseargs::getarg () { skiplwsp (); if (p >= lim || *p == '\n') return NULL; bool q = false; vec arg; for (;;) { if (*p == '\\') { if (p + 1 >= lim) { error ("invalid '\\' before end of file"); return NULL; } else if (p[1] == '\n') skiplwsp (); else { arg.push_back (p[1]); p += 2; } continue; } if (p >= lim) { if (q) error ("closing '\"' missing"); return str (arg.base (), arg.size ()); } if (*p == '\"') q = !q; else if (q || !isspc (*p)) arg.push_back (*p); else return str (arg.base (), arg.size ()); p++; } return NULL; // XXX - egcs bug } bool parseargs::getline (vec *args, int *linep) { args->setsize (0); skipblanks (); if (linep) *linep = lineno; while (str s = getarg ()) args->push_back (s); return args->size (); } void parseargs::error (str msg) { strbuf pref; if (filename) pref << filename << ":"; if (lineno) pref << lineno << ": "; else pref << " "; fatal << pref << msg << "\n"; } parseargs::parseargs (str file, int fd) : buf (errorbuf), lim (buf), p (buf), filename (file), lineno (0) { if (fd == -1 && (fd = open (file, O_RDONLY, 0)) < 0) error (strbuf ("%m")); // XXX - should fstat fd for initial size, to optimize for common case // when fd is for a regular file. size_t pos = 0; size_t size = 128; buf = static_cast (xmalloc (size)); for (;;) { ssize_t n = read (fd, buf + pos, size - pos); if (n < 0) { error (strbuf ("%m")); close (fd); return; } if (!n) { p = buf; lim = buf + pos; lineno = 1; close (fd); return; } pos += n; if (pos == size) size <<= 1; buf = static_cast (xrealloc (buf, size)); } } parseargs::~parseargs () { if (buf != errorbuf) xfree (buf); }