Index: trunk/psModules/src/concepts/pmConcepts.c
===================================================================
--- trunk/psModules/src/concepts/pmConcepts.c	(revision 17850)
+++ trunk/psModules/src/concepts/pmConcepts.c	(revision 17864)
@@ -1198,2 +1198,76 @@
     return true;
 }
+
+
+// Interpolate the concept.  Generalises the FPA/Chip/Cell
+#define CONCEPT_INTERPOLATE(SOURCE, NAME) \
+    if (strncmp(concept, NAME, 4) == 0) { \
+        if (!(SOURCE)) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot interpolate %s because %s not provided", \
+                    concept, NAME); \
+            psFree(string); \
+            return NULL; \
+        } \
+        psMetadataItem *item = psMetadataLookup((SOURCE)->concepts, concept); /* Item with concept */ \
+        if (!item) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't find concept %s in %s", concept, NAME); \
+            psFree(string); \
+            return NULL; \
+        } \
+        \
+        psString value = psMetadataItemParseString(item); /* Value of concept */ \
+        if (!value) { \
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s", concept); \
+            psFree(string); \
+            return NULL; \
+        } \
+        \
+        char replace[length + 2];       /* String to replace with value */ \
+        replace[0] = '{'; \
+        strcpy(replace + 1, concept); \
+        strcpy(replace + length + 1, "}"); \
+        \
+        psTrace("psModules.concepts", 10, "Interpolating concept %s for %s", replace, value); \
+        \
+        if (!psStringSubstitute(&string, value, replace)) { \
+            psError(PS_ERR_UNKNOWN, false, "Unable to replace concept %s", concept); \
+            psFree(string); \
+            return NULL; \
+        } \
+        \
+        continue; \
+    }
+
+
+// XXX Could make the concept delimiters, currently '{' and '}', configurable
+psString pmConceptsInterpolate(const char *input,
+                               const pmFPA *fpa,
+                               const pmChip *chip,
+                               const pmCell *cell
+    )
+{
+    PS_ASSERT_STRING_NON_EMPTY(input, NULL);
+
+    psString string = psStringCopy(input); // Interpolated string, to return
+
+    char *start;                        // Start of a concept
+    while ((start = strchr(string, '{'))) {
+        char *stop = strchr(start, '}'); // End of a concept
+        int length = stop - start;      // Length of the concept name, including terminating \0
+        char concept[length];  // Name of concept
+        strncpy(concept, start + 1, length - 1);
+        concept[length] = '\0';
+
+        psTrace("psModules.concepts", 7, "Interpolating concept %s", concept);
+
+        CONCEPT_INTERPOLATE(fpa,  "FPA");
+        CONCEPT_INTERPOLATE(chip, "CHIP");
+        CONCEPT_INTERPOLATE(cell, "CELL");
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised concept: %s", concept);
+        psFree(string);
+        return NULL;
+    }
+
+    return string;
+}
Index: trunk/psModules/src/concepts/pmConcepts.h
===================================================================
--- trunk/psModules/src/concepts/pmConcepts.h	(revision 17850)
+++ trunk/psModules/src/concepts/pmConcepts.h	(revision 17864)
@@ -4,6 +4,6 @@
  * @author Paul Price, IfA
  *
- * @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
- * @date $Date: 2008-02-15 02:26:09 $
+ * @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-05-30 21:35:51 $
  * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
  */
@@ -222,4 +222,13 @@
     );
 
+/// Interpolate a concept name to the actual value
+///
+/// Concepts enclosed within braces {}, are replaced with the value of the concept
+psString pmConceptsInterpolate(const char *input, ///< Input string
+                               const pmFPA *fpa, ///< FPA with concept values, or NULL
+                               const pmChip *chip, ///< Chip with concept values, or NULL
+                               const pmCell *cell ///< Cell with concept values, or NULL
+    );
+
 
 /// @}
