Код:
Молодой человек! Сколько еще раз мы с вами будем "пулять по Луне"?
Вы же в условии не сказали, что результат взаимодействия последнего
вещества пробирки и очередного опытного вещества СНОВА должен
проверяться по формуле взаимодействия!
Эта ситуация учтена в тексте программы, обрамленная комментариями
//((((( и //)))))
Кроме того добавлен контроль на пустоту введенного имени файла
(см. функцию IsEmpty)
И если программирование будет хотя бы отчасти вашей будущей профессией,
запомните: в программировании четко поставленная задача есть 70 процентов
решения задачи...
#include <stdio.h>
#include <malloc.h>
#include <string.h>
struct formula
{
  char *n1;  // Первое вещество из пары взаимодействующих веществ
  char *n2;  // Второе вещество из пары взаимодействующих веществ
  char *n3;  // Результат их взаимодействия
};
#define MAX_FORMULA 200 // Максимальное число формул
#define MAX_PRIOR   100 // Максимальное число опытных веществ
#define MAX_LEN 512
// Список формул
struct formula prior_formula[MAX_FORMULA];
// Число формул
long  cnt_formula;
// Число опытных веществ
long  cnt_prior;
// Исходные вещества для опыта
char *prior[MAX_PRIOR];
// Вещества в результате опыта
char *result[MAX_PRIOR];
void stack_func
(
  struct formula  *para,    // Список формул взаимодействий веществ
  long            len_para, // Размер списка формул
  char            **src,    // Список веществ для опыта
  long            len_src,  // Размер списка веществ для опыта
  char            **out,    // Адрес результирующего списка веществ
                            //  после окончания опыта
  long            *len_out  // Размер списка веществ в результате опыта
)
{
  long  i, j, d1, d2;
  char  *n1, *n2;
  // Поместить в массив результата первое вещество
  out[0] = src[0];
  d1 = d2 = 0;
  // Просмотр всех остальных веществ
  for(i = 1; i < len_src; ++i)
  {
    n1 = out[d1];    // Последнее вещество из пробирки
    n2 = src[++d2];  // Очередное вещество для опыта
    // Поиск формулы взаимодействия этих веществ
    for(j = 0; j < len_para; ++j)
    {
      if(strcmp(n1, para[j].n1) == 0 && strcmp(n2, para[j].n2) == 0 ||
         strcmp(n2, para[j].n1) == 0 && strcmp(n1, para[j].n2) == 0)
        break;
    }
    // Если формулу взаимодействия нашли, заменить
    // последнее вещество из пробирки на результат взаимодействия
    if(j != len_para)
    {
      out[d1] = para[j].n3;
//(((((
      // Кроме того, результат взаимодействия может спровоцировать новое
      // взаимодействие, поэтому повторяем поиск формулы взаимодействия
      // для последних двух веществ
      while(d1)
      {
        n1 = out[d1 - 1]; // Предпоследнее вещество в пробирке
        n2 = out[d1];     // Последнее вещество в пробирке
        for(j = 0; j < len_para; ++j)
        {
          if(strcmp(n1, para[j].n1) == 0 && strcmp(n2, para[j].n2) == 0 ||
             strcmp(n2, para[j].n1) == 0 && strcmp(n1, para[j].n2) == 0)
            break;
        }
        if(j == len_para)
          break;
        out[--d1] = para[j].n3;
      }
//)))))
      continue;
    }
    // Формулу не нашли, последним веществом в пробирке будет
    // очередное выбранное вещество
    out[++d1] = n2;
  }
  *len_out = d1 + 1;
}
// Проверить на пустоту введенные имена файлов
int IsEmpty(char *s)
{
  char c, *s1, tmp[MAX_LEN];
  for(s1 = s; (c = *s) == ' ' || c == '\n' || c == '\t'; ++s)
    ;
  if(c == 0)
  {
    printf("Empty Name File\n");
    return(1);
  }
  // Переписать имя файла без начальных пробелов
  // Это можно было сделать проще (через memmove),
  // но я не хочу еще одной дискуссии по этому поводу
  strcpy(tmp, s);
  strcpy(s1, tmp);
  return(0);
}
int main(int argc, char **argv)
{
  long len, i, len1, len2, len3;
  FILE *f;
  char  feld[MAX_LEN + 1];
  char  c, *s, *s1, *s2, *s3;
  char  name_formula[MAX_LEN], name_prior[MAX_LEN], name_out[MAX_LEN];
  // Программа стартуется без параметров,
  // а имена файлов вводятся с консоли
  //
  // 1) имя файла с формулами взаимодействия
  // 2) имя файла с опытными веществами
  // 3) имя файла результата
  // Ввод данных о формулах взаимодействия веществ из файла,
  // структура которого предполагается следующей:
  //
  // Каждая строка файла представляет собой одну формулу взаимодействия
  // и состоит из трех слов, разделенных пробелами, например:
  //
  // свинец соляная_кислота супер_вещество
  //
  // Обратите внимание на то, что в названии веществ не должно быть
  // пробела, при необходимост последнего следует использовать символ
  // подчеркивания
  //
  // В приведенном выше примере предполагается, что если в пробирке
  // последнее вещество есть свинец, то при добавлении в пробирку
  // соляная_кислота с этим свинцом превращается в супер_вещество
  //
  // Конец файла означает конец формул взаимодействия
  // Запрос имен трех файлов с консоли
  // Не предполагается никаких проверок (переполнение и так далее)
  printf("Formula File? ");
  gets(name_formula);
  if(IsEmpty(name_formula))
    return(0);
  printf("Prior File?   ");
  gets(name_prior);
  if(IsEmpty(name_prior))
    return(0);
  printf("Result File?  ");
  gets(name_out);
  if(IsEmpty(name_out))
    return(0);
  // А есть ли файл с формулами?
  if((f = fopen(name_formula, "r")) == NULL)
  {
    printf("Not File (%s) Found\n", name_formula);
    return(0);
  }
  // Номер строки файла для выдачи ошибок
  i = 0;
  // Счетчик формул
  cnt_formula = 0;
  
  // Счетчик опытных веществ
  cnt_prior = 0;
  // Читаем символьный файл по записям до его конца
  while(fgets(feld, MAX_LEN, f))
  {
    ++i;
    // Сбрасываем пустые символы перед первым веществом
    for(s = feld; *s == ' ' || *s == '\t' || *s == '\n'; ++s)
      ;
    // Считаем, что если формула начинается с символа ';'
    // то это комментарийная строка
    // Комментарием будет также полностью пустая строка
    if(*s == ';' || *s == 0)
      continue;
    // Не слишком ли много формул задано?
    if(cnt_formula == MAX_FORMULA)
    {
      printf("Error Big Formula\n");
      goto error1;
    }
    // Отмечаем начало первого слова во введенной записи
    s1 = s;
    // Ищем конец первого слова
    while((c = *++s) != ' ' && c != '\t' && c != '\n' && c)
      ;
    // Записываем в конец первого слова символ конца строки
    *s = 0;
    // А есть ли еще слова после первого слова?
    if(c == 0)
    {
error:
      printf("Error Formula Line %d\n", i);
error1:
      fclose(f);
error2:
      // Освобождаем память
      for(i = 0; i < cnt_formula; ++i)
        free(prior_formula[i].n1);
      return(0);
    }
    // Сбрасываем пустые символы перед вторым веществом
    while((c = *++s) == ' ' || c == '\t' || c == '\n')
      ;
    // А есть ли второе вещество?
    if(c == 0)
      goto error;        
    // Отмечаем начало второго слова во введенной записи
    s2 = s;
    // Ищем конец второго слова
    while((c = *++s) != ' ' && c != '\t' && c != '\n' && c)
      ;
    // Записываем в конец второго слова символ конца строки
    *s = 0;
    // А есть ли еще слова после второго слова?
    if(c == 0)
      goto error;        
    // Сбрасываем пустые символы перед третьим веществом
    while((c = *++s) == ' ' || c == '\t' || c == '\n')
      ;
    // Отмечаем начало третьего слова во введенной записи
    s3 = s;
    // Ищем конец третьего слова
    while((c = *++s) != ' ' && c != '\t' && c != '\n' && c)
      ;
    // Записываем в конец третьего слова символ конца строки
    *s = 0;
    // После третьего слова до конца записи могут идти только
    // пустые символы
    if(c)
    {
      while((c = *++s) == ' ' || c == '\t' || c == '\n')
        ;
      if(c)
        goto error;
    }
    // Теперь можно выделить память под три слова и запомнить их там
    // Считаем, что память есть всегда, поэтому не применяем контроль
    len1 = strlen(s1) + 1;
    len2 = strlen(s2) + 1;
    len3 = strlen(s3) + 1;
    s = (char *)malloc(len1 + len2 + len3);
    prior_formula[cnt_formula].n1 = s;
    prior_formula[cnt_formula].n2 = s + len1;
    prior_formula[cnt_formula].n3 = s + len1 + len2;
    strcpy(prior_formula[cnt_formula].n1, s1);
    strcpy(prior_formula[cnt_formula].n2, s2);
    strcpy(prior_formula[cnt_formula].n3, s3);
    // Увеличиваем счетчик формул
    ++cnt_formula;
  }
  // А есть ли хотя бы одна формула?
  if(cnt_formula == 0)
  {
    printf("Error No Formula\n");
    goto error1;
  }
  // С формулами покончено, закрываем их файл
  fclose(f);
  // Ввод данных о веществах, участвующих в опыте
  // Считается, что они тоже заданы словами, разделенными пробелами,
  // но для упрощения программы будем их вводить при помощи fscanf,
  // а не заниматься разбором, как для формул
  // А есть ли файл с опытными веществами?
  if((f = fopen(name_prior, "r")) == NULL)
  {
    printf("Not File (%s) Found\n", name_prior);
    goto error2;
  }
  while(fscanf(f, "%s", feld) == 1)
  {
    if(cnt_prior == MAX_PRIOR)
    {
      printf("Error Big Prior\n");
      // Освобождаем память
      for(i = 0; i < cnt_prior; ++i)
        free(prior[i]);
      goto error1;
    }
    prior[cnt_prior] = (char *)malloc(strlen(feld) + 1);
    strcpy(prior[cnt_prior], feld);
    ++cnt_prior;
  }
  // А есть ли опытные вещества?
  if(cnt_prior == 0)
  {
    printf("Error No Prior\n");
    goto error1;
  }
  // А не слишком ли мало опытных веществ?
  if(cnt_prior < 2)
  {
    printf("Error 2 Prior\n");
    goto error1;
  }
  // С опытными веществами покончено, закрываем их файл
  fclose(f);
  stack_func(prior_formula, cnt_formula, prior, cnt_prior, result, &len);
  // Открыть файл печати результата
  f = fopen(name_out, "w");
  // Печать результата опыта
  for(i = 0; i < len; ++i)
  {
    fprintf(f, "%s ", result[i]);
    if(i + 1 % 5 == 0)
      fprintf(f, "\n");
  }
  fprintf(f, "\n");
  fclose(f);
  // Освободить память
  for(i = 0; i < cnt_formula; ++i)
    free(prior_formula[i].n1);
  for(i = 0; i < cnt_prior; ++i)
    free(prior[i]);
  return(0);
}