Logo Search packages:      
Sourcecode: ksh version File versions

mamold.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1989-2007 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Bell Laboratories
 *
 * convert a make abstract machine stream on stdin
 * to an oldmake makefile on stdout
 */

static const char usage[] =
"[-?\n@(#)$Id: mamold (AT&T Research) 1989-03-22 $\n]"
USAGE_LICENSE
"[+NAME?mamold - make abstract machine to oldmake makefile conversion filter]"
"[+DESCRIPTION?\bmamold\b reads MAM (Make Abstract Machine) target and"
"     prerequisite file descriptions from the standard input and writes"
"     an equivalent \bgmake\b(1) makefile on the standard output. Mamfiles"
"     are generated by the \b--mam\b option of \bnmake\b(1) and"
"     \bgmake\b(1).]"
"[+?Symbolic information is preserved where possible in the output makefile."
"     Comments, however, are lost in the conversion. Recursive makefiles"
"     are not converted; each makefile level must be converted separately.]"

"[d:debug?]#[level]"
"[g:graph?Verify the dependency graph but do not generate a makefile on"
"     the standard output.]"
"[h:header?Use \atext\as instead of the default for the generated makefile"
"     header.]#[text]"
"[x:omit?Omit pathnames with directory \aprefix\a.]#[prefix]"

"[+SEE ALSO?\bmamnew\b(1), \bgmake\b(1), \bnmake\b(1)]"
"[+REFERENCES]{"
"     [+A Make abstract machine, Glenn Fowler, 1994,?"
"           http://www.research.att.com/~gsf/mam/]"
"}"
;

#include <ast.h>
#include <mam.h>
#include <ctype.h>
#include <error.h>
#include <stdio.h>

#define LONGLINE  72          /* too long output line length      */

#define A_listprereq    (A_LAST<<1) /* prereqs listed       */
#define A_listtarg      (A_LAST<<2) /* listed as target           */

struct state                        /* program state        */
{
      int         graph;            /* output dependency graph info     */
      int         header;           /* header supplied            */
      int         heredoc;    /* last value had <<          */
      struct mam* mam;        /* make abstract machine info */
      struct block*     omit;       /* dir prefixes to omit       */
};

static struct state     state;

/*
 * clear listprereq for all prerequisites
 */

static void
clrprereqs(register struct rule* r)
{
      register struct list*   p;

      r->attributes &= ~A_listprereq;
      for (p = r->prereqs; p; p = p->next)
            if (p->rule->attributes & A_listprereq)
                  clrprereqs(p->rule);
      for (p = r->implicit; p; p = p->next)
            if (p->rule->attributes & A_listprereq)
                  clrprereqs(p->rule);
}

/*
 * dump a value that may be expanded by oldmake
 */

static int
dumpvalue(register int col, register char* s, int sep)
{
      register int      c;
      register char*    v;
      int         dollar;
      int         escape = 0;
      int         quote = 0;

      if (sep)
      {
            sfputc(sfstdout, sep);
            col++;
      }
      for (;;)
            switch (c = *s++)
            {
            case 0:
                  if (sep && sep != '\t')
                  {
                        col = 1;
                        sfputc(sfstdout, '\n');
                  }
                  return col;
            case ' ':
            case '\t':
                  if (sep == '\t' && state.heredoc || col < LONGLINE - 8)
                        goto emit;
                  while (isspace(*s))
                        s++;
                  if (*s)
                  {
                        sfputr(sfstdout, " \\\n\t", -1);
                        col = 8;
                  }
                  break;
            case '#':
                  sfputr(sfstdout, "$(sharp)", -1);
                  col += 8;
                  break;
            case '<':
                  if (sep == '\t' && *s == c)
                        state.heredoc = 1;
                  goto emit;
            case '\'':
                  quote = !quote;
                  goto emit;
            case '\\':
                  if (*s != '$' && *s != '\'')
                        goto emit;
                  s++;
                  escape = -1;
                  /*FALLTHROUGH*/
            case '$':
                  escape++;
                  dollar = 1;
                  if (isalpha(*s) || *s == '_')
                  {
                        for (v = s; isalnum(*v) || *v == '_'; v++);
                        c = *v;
                        *v = 0;
                        if (getvar(state.mam->main, s))
                        {
                              sfprintf(sfstdout, "$(%s)", s);
                              col += (v - s) + 3;
                              *(s = v) = c;
                              escape = 0;
                              break;
                        }
                        *v = c;
                  }
                  if (escape)
                  {
                        escape = 0;
                        if (!quote)
                              switch (*s)
                              {
                              case '{':
                                    if (*(v = s + 1))
                                          v++;
                                    while (isalnum(*v) || *v == '_')
                                          v++;
                                    switch (*v)
                                    {
                                    case ':':
                                    case '-':
                                    case '+':
                                          break;
                                    default:
                                          dollar = 0;
                                          break;
                                    }
                                    break;
                              case '$':
                                    s--;
                                    do
                                    {
                                          sfputc(sfstdout, '$');
                                          col++;
                                    } while (*++s == '$');
                                    break;
                              default:
                                    sfputc(sfstdout, '\\');
                                    col++;
                                    break;
                              }
                  }
                  else
                        dollar = 0;
                  c = '$';
                  if (dollar)
                  {
                        sfputc(sfstdout, c);
                        col++;
                  }
                  /*FALLTHROUGH*/
            default:
            emit:
                  sfputc(sfstdout, c);
                  col++;
                  break;
            }
}

/*
 * dump a name keeping track of the right margin
 */

static int
dumpname(int col, char* s)
{
      register int      n;

      if (!state.graph)
      {
            n = strlen(s);
            if (col + n >= LONGLINE)
            {
                  sfputr(sfstdout, " \\\n\t\t", -1);
                  col = 16;
            }
            else if (col <= 1)
                  col = 1;
            else
            {
                  sfputc(sfstdout, ' ');
                  col++;
            }
            col += n;
      }
      else if (col++ > 1)
            sfputc(sfstdout, ' ');
      dumpvalue(0, s, 0);
      return col;
}

/*
 * dump an action
 */

static void
dumpaction(register struct block* p)
{
      if (p)
      {
            state.heredoc = 0;
            for (;;)
            {
                  dumpvalue(0, p->data, '\t');
                  if (!(p = p->next))
                        break;
                  sfputr(sfstdout, "$(newline)", -1);
                  if (!state.heredoc)
                        sfputr(sfstdout, " \\\n", -1);
            }
            sfputc(sfstdout, '\n');
      }
}

/*
 * dump r and its implicit prerequisites
 */

static int
dumpprereqs(register int col, register struct rule* r)
{
      register struct block*  d;
      register struct list*   p;

      if (!(r->attributes & A_listprereq))
      {
            r->attributes |= A_listprereq;
            for (d = state.omit; d; d = d->next)
                  if (strmatch(r->name, d->data))
                        return col;
            col = dumpname(col, r->name);
            for (p = r->implicit; p; p = p->next)
                  col = dumpprereqs(col, p->rule);
      }
      return col;
}

/*
 * dump the rules
 */

static void
dump(register struct rule* r)
{
      register int            col;
      register struct list*   p;

      if (!(r->attributes & (A_listtarg|A_metarule)))
      {
            r->attributes |= A_listtarg;
            if (r->action || r->prereqs)
            {
                  clrprereqs(r);
                  r->attributes |= A_listprereq;
                  sfputc(sfstdout, '\n');
                  col = dumpname(1, r->name);
                  col = dumpname(col, ":");
                  for (p = r->prereqs; p; p = p->next)
                        if (!(p->rule->attributes & A_listprereq))
                        {
                              clrprereqs(p->rule);
                              col = dumpprereqs(col, p->rule);
                        }
                  sfputc(sfstdout, '\n');
                  if (!state.graph)
                        dumpaction(r->action);
            }
            for (p = r->prereqs; p; p = p->next)
                  if (p->rule != r)
                        dump(p->rule);
            for (p = r->implicit; p; p = p->next)
                  if (p->rule != r)
                        dump(p->rule);
      }
}

/*
 * dump var definition
 */

static int
dumpvar(const char* an, char* av, void* handle)
{
      char*       name = (char*)an;
      struct var* v = (struct var*)av;
      register char*    s;
      register char*    t;
      register int      c;

      NoP(handle);
      if (*v->value)
      {
            s = t = v->value;
            while (c = *t++ = *s++)
            {
                  if (c == '\\')
                  {
                        if (!(*t++ = *s++))
                              break;
                  }
                  else if (c == '"')
                        t--;
            }
            dumpvalue(dumpname(0, name), v->value, '=');
      }
      return 0;
}

/*
 * add prefix to list of dir prefixes to omit
 */

static void
omit(char* prefix)
{
      int         n;
      struct block*     p;

      n = strlen(prefix);
      p = newof(0, struct block, 1, n + 1);
      strcpy(p->data = (char*)p + sizeof(struct block), prefix);
      strcpy(p->data + n, "*");
      p->next = state.omit;
      state.omit = p;
}

int
main(int argc, char** argv)
{
      register struct list*   p;

      NoP(argc);
      error_info.id = "mamold";
      for (;;)
      {
            switch (optget(argv, usage))
            {
            case 'd':
                  error_info.trace = -opt_info.num;
                  continue;
            case 'g':
                  state.graph = 1;
                  continue;
            case 'h':
                  state.header = 1;
                  sfputr(sfstdout, opt_info.arg, '\n');
                  continue;
            case 'x':
                  omit(opt_info.arg);
                  continue;
            case '?':
                  error(ERROR_USAGE|4, opt_info.arg);
                  break;
            case ':':
                  error(2, opt_info.arg);
                  break;
            }
            break;
      }
      if (error_info.errors)
            error(ERROR_USAGE|4, "%s", optusage(NiL));

      /*
       * initialize
       */

      omit("/usr/include");
      omit("/");

      /*
       * scan, collect and dump
       */

      if (!state.graph && !state.header)
      {
            sfprintf(sfstdout, "# # oldmake makefile generated by mamold # #\n");
            sfprintf(sfstdout, "# oldmake ... null='' sharp='$(null)#' newline='$(null)\n");
            sfprintf(sfstdout, "# '\n");
            sfprintf(sfstdout, "newline=;\n");
      }
      if (!(state.mam = mamalloc()))
            error(3, "cannot initialize");
      mamvar(state.mam->main, "HOME", "");
      mamvar(state.mam->main, "newline", "");
      if (mamscan(state.mam, NiL) < 0)
            error(3, "invalid input");
      if (!state.graph)
            hashwalk(state.mam->main->vars, 0, dumpvar, NiL);
      for (p = state.mam->main->root->prereqs; p; p = p->next)
            dump(p->rule);
      exit(error_info.errors != 0);
}

Generated by  Doxygen 1.6.0   Back to index