/*
 * libdpkg - Debian packaging suite library routines
 * fields.c - parsing of all the different fields, when reading in
 *
 * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
 *
 * This 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 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 dpkg; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

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

#include "config.h"
#include "dpkg.h"
#include "dpkg-db.h"
#include "parsedump.h"

static int convert_string(const char *filename, int lno, const char *what, int otherwise,
                          FILE *warnto, int *warncount, const struct pkginfo *pigp,
                          const char *startp, const struct namevalue *nvip,
                          const char **endpp) {
  const char *ep;
  int c, l;

  ep= startp;
  if (!*ep) parseerr(0,filename,lno, warnto,warncount,pigp,0, "%s is missing",what);
  while ((c= *ep) && !isspace(c)) ep++;
  l= (int)(ep-startp);
  while (nvip->name && (strncasecmp(nvip->name,startp,l) || nvip->name[l])) nvip++;
  if (!nvip->name) {
    if (otherwise != -1) return otherwise;
    parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%.*s' is not allowed for %s",
             l > 50 ? 50 : l, startp, what);
  }
  while (isspace(c)) c= *++ep;
  if (c && !endpp)
    parseerr(0,filename,lno, warnto,warncount,pigp,0, "junk after %s",what);
  if (endpp) *endpp= ep;
  return nvip->value;
}

void f_name(struct pkginfo *pigp, struct pkginfoperfile *pifp, enum parsedbflags flags,
            const char *filename, int lno, FILE *warnto, int *warncount,
            const char *value, const struct fieldinfo *fip) {
  const char *e;
  if ((e= illegal_packagename(value,0)) != 0)
    parseerr(0,filename,lno, warnto,warncount,pigp,0, "invalid package name (%s)",e);
  pigp->name= nfstrsave(value);
  findpackage(value); /* We discard the value from findpackage, but calling it
                       * forces an entry in the hash table to be made if it isn't
                       * already.  This is so that we don't reorder the file
                       * unnecessarily (doing so is bad for debugging).
                       */
}

void f_charfield(struct pkginfo *pigp, struct pkginfoperfile *pifp,
                 enum parsedbflags flags,
                 const char *filename, int lno, FILE *warnto, int *warncount,
                 const char *value, const struct fieldinfo *fip) {
  if (*value) PKGPFIELD(pifp,fip->integer,char*)= nfstrsave(value);
}

void f_boolean(struct pkginfo *pigp, struct pkginfoperfile *pifp,
               enum parsedbflags flags,
               const char *filename, int lno, FILE *warnto, int *warncount,
               const char *value, const struct fieldinfo *fip) {
  pifp->essential=
    *value ? convert_string(filename,lno,"yes/no in `essential' field", -1,
                            warnto,warncount,pigp,
                            value,booleaninfos,0)
           : 0;
}

void f_section(struct pkginfo *pigp, struct pkginfoperfile *pifp,
               enum parsedbflags flags,
               const char *filename, int lno, FILE *warnto, int *warncount,
               const char *value, const struct fieldinfo *fip) {
  if (!*value) return;
  pigp->section= nfstrsave(value);
}

void f_priority(struct pkginfo *pigp, struct pkginfoperfile *pifp,
                enum parsedbflags flags,
                const char *filename, int lno, FILE *warnto, int *warncount,
                const char *value, const struct fieldinfo *fip) {
  if (!*value) return;
  pigp->priority= convert_string(filename,lno,"word in `priority' field", pri_other,
                             warnto,warncount,pigp,
                             value,priorityinfos,0);
  if (pigp->priority == pri_other) pigp->otherpriority= nfstrsave(value);
}

void f_status(struct pkginfo *pigp, struct pkginfoperfile *pifp,
              enum parsedbflags flags,
              const char *filename, int lno, FILE *warnto, int *warncount,
              const char *value, const struct fieldinfo *fip) {
  const char *ep;

  if (flags & pdb_rejectstatus)
    parseerr(0,filename,lno, warnto,warncount,pigp,0,
             "value for `status' field not allowed in this context");
  if (flags & pdb_recordavailable) return;
  
  pigp->want= convert_string(filename,lno,"first (want) word in `status' field", -1,
                             warnto,warncount,pigp, value,wantinfos,&ep);
  pigp->eflag= convert_string(filename,lno,"second (error) word in `status' field", -1,
                              warnto,warncount,pigp, ep,eflaginfos,&ep);
  pigp->status= convert_string(filename,lno,"third (status) word in `status' field", -1,
                               warnto,warncount,pigp, ep,statusinfos,0);
}

void f_conffiles(struct pkginfo *pigp, struct pkginfoperfile *pifp,
                 enum parsedbflags flags,
                 const char *filename, int lno, FILE *warnto, int *warncount,
                 const char *value, const struct fieldinfo *fip) {
  struct conffile **lastp, *newlink;
  const char *endent, *endfn;
  int c, namelen, hashlen;
  
  lastp= &pifp->conffiles;
  while (*value) {
    c= *value++;
    if (c == '\n') continue;
    if (c != ' ') parseerr(0,filename,lno, warnto,warncount,pigp,0, "value for"
                           " `conffiles' has line starting with non-space `%c'", c);
    for (endent= value; (c= *endent)!=0 && c != '\n'; endent++);
    for (endfn= endent; *endfn != ' '; endfn--);
    if (endfn <= value+1 || endfn >= endent-1)
      parseerr(0,filename,lno, warnto,warncount,pigp,0,
               "value for `conffiles' has malformatted line `%.*s'",
               (int)(endent-value > 250 ? 250 : endent-value), value);
    newlink= nfmalloc(sizeof(struct conffile));
    value= skip_slash_dotslash(value);
    namelen= (int)(endfn-value);
    if (namelen <= 0) parseerr(0,filename,lno, warnto,warncount,pigp,0,
                               "root or null directory is listed as a conffile");
    newlink->name= nfmalloc(namelen+2);
    newlink->name[0]= '/';
    memcpy(newlink->name+1,value,namelen);
    newlink->name[namelen+1]= 0;
    hashlen= (int)(endent-endfn)-1; newlink->hash= nfmalloc(hashlen+1);
    memcpy(newlink->hash,endfn+1,hashlen); newlink->hash[hashlen]= 0;
    newlink->next =0;
    *lastp= newlink;
    lastp= &newlink->next;
    value= endent;
  }
}

void f_dependency(struct pkginfo *pigp, struct pkginfoperfile *pifp,
                  enum parsedbflags flags,
                  const char *filename, int lno, FILE *warnto, int *warncount,
                  const char *value, const struct fieldinfo *fip) {
  char *q;
  const char *p, *emsg;
  struct varbuf depname, version;
  struct dependency *dyp, **ldypp;
  struct deppossi *dop, **ldopp;

  if (!*value) return; /* empty fields are ignored */
  varbufinit(&depname); varbufinit(&version);
  p= value;
  ldypp= &pifp->depends; while (*ldypp) ldypp= &(*ldypp)->next;
  for (;;) { /* loop creating new struct dependency's */
    dyp= nfmalloc(sizeof(struct dependency));
    dyp->up= 0; /* Set this to zero for now, as we don't know what our real
                 * struct pkginfo address (in the database) is going to be yet.
                 */
    dyp->next= 0; *ldypp= dyp; ldypp= &dyp->next;
    dyp->list= 0; ldopp= &dyp->list;
    dyp->type= fip->integer;
    for (;;) { /* loop creating new struct deppossi's */
      varbufreset(&depname);
      while (*p && !isspace(*p) && *p != '(' && *p != ',' && *p != '|') {
        varbufaddc(&depname,*p); p++;
      }
      varbufaddc(&depname,0);
      if (!*depname.buf)
        parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field, missing"
                 " package name, or garbage where package name expected", fip->name);
      emsg= illegal_packagename(depname.buf,0);
      if (emsg) parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field,"
                         " invalid package name `%.255s': %s",
                         fip->name, depname.buf, emsg);
      dop= nfmalloc(sizeof(struct deppossi));
      dop->up= dyp;
      dop->ed= findpackage(depname.buf);
      dop->next= 0; *ldopp= dop; ldopp= &dop->next;
      dop->nextrev= 0; /* Don't link this (which is after all only `newpig' from
      dop->backrev= 0;  * the main parsing loop in parsedb) into the depended on
                        * packages' lists yet.  This will be done later when we
                        * install this (in parse.c).  For the moment we do the
                        * `forward' links in deppossi (`ed') only, and the backward
                        * links from the depended on packages to dop are left undone.
                        */
      dop->cyclebreak= 0;
      while (isspace(*p)) p++;
      if (*p == '(') {
        varbufreset(&version);
        p++; while (isspace(*p)) p++;
        switch (*p) {
        case '<': dop->verrel= dvr_earlier; p++; break;
        case '>': dop->verrel= dvr_later;   p++; break;
        case '=': dop->verrel= dvr_exact;   p++; break;
        default: dop->verrel= dvr_exact;
        }
        while (isspace(*p)) p++;
        while (*p && *p != ')' && *p != '(') {
          if (!isspace(*p)) varbufaddc(&version,*p);
          p++;
        }
        if (*p == '(') parseerr(0,filename,lno, warnto,warncount,pigp,0,
                                "`%s' field, reference to `%.255s'"
                                " version contains (", fip->name, depname.buf);
        else if (*p == 0) parseerr(0,filename,lno, warnto,warncount,pigp,0,
                                   "`%s' field, reference to `%.255s'"
                                   "version unterminated", fip->name, depname.buf);
        varbufaddc(&version,0);
        q= strrchr(version.buf,'-');
        if (q) {
          *q++= 0;
          dop->revision= nfstrsave(q);
        } else {
          dop->revision= 0;
        }
        dop->version= nfstrsave(version.buf);
        p++; while (isspace(*p)) p++;
      } else {
        dop->verrel= dvr_none;
        dop->revision= dop->version= 0;
      }
      if (!*p || *p == ',') break;
      if (*p != '|')
        parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field, syntax"
                 " error after reference to package `%.255s'",
                 fip->name, dop->ed->name);
      if (fip->integer == dep_conflicts || fip->integer == dep_provides)
        parseerr(0,filename,lno, warnto,warncount,pigp,0,
                 "alternatives (`|') not allowed in %s field",
                 fip->name);
      p++; while (isspace(*p)) p++;
    }
    if (!*p) break;
    p++; while (isspace(*p)) p++;
  }
}

