1 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for strdup() */ 2 #include <petsc/private/petscimpl.h> /*I "petscsys.h" I*/ 3 4 #if defined(PETSC_HAVE_YAML) 5 #include <yaml.h> /* use external LibYAML */ 6 #else 7 #include <../src/sys/yaml/include/yaml.h> 8 #endif 9 10 static MPI_Comm petsc_yaml_comm = MPI_COMM_NULL; /* only used for parallel error handling */ 11 12 static inline MPI_Comm PetscYAMLGetComm(void) 13 { 14 return PetscLikely(petsc_yaml_comm != MPI_COMM_NULL) ? petsc_yaml_comm : (petsc_yaml_comm = PETSC_COMM_SELF); 15 } 16 17 static inline MPI_Comm PetscYAMLSetComm(MPI_Comm comm) 18 { 19 MPI_Comm prev = PetscYAMLGetComm(); petsc_yaml_comm = comm; return prev; 20 } 21 22 #define TAG(node) ((const char *)((node)->tag)) 23 #define STR(node) ((const char *)((node)->data.scalar.value)) 24 #define SEQ(node) ((node)->data.sequence.items) 25 #define MAP(node) ((node)->data.mapping.pairs) 26 27 static PetscErrorCode PetscParseLayerYAML(PetscOptions options, yaml_document_t *doc, yaml_node_t *node) 28 { 29 MPI_Comm comm = PetscYAMLGetComm(); 30 char name[PETSC_MAX_OPTION_NAME] = "", prefix[PETSC_MAX_OPTION_NAME] = ""; 31 32 PetscFunctionBegin; 33 if (node->type == YAML_SCALAR_NODE && !STR(node)[0]) PetscFunctionReturn(0); /* empty */ 34 PetscCheck(node->type == YAML_MAPPING_NODE,comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping"); 35 for (yaml_node_pair_t *pair = MAP(node).start; pair < MAP(node).top; pair++) { 36 yaml_node_t *keynode = yaml_document_get_node(doc, pair->key); 37 yaml_node_t *valnode = yaml_document_get_node(doc, pair->value); 38 PetscBool isMergeKey,isDummyKey,isIncludeTag; 39 40 PetscCheck(keynode,comm, PETSC_ERR_LIB, "Corrupt YAML document"); 41 PetscCheck(valnode,comm, PETSC_ERR_LIB, "Corrupt YAML document"); 42 PetscCheck(keynode->type == YAML_SCALAR_NODE,comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar"); 43 44 /* "<<" is the merge key: don't increment the prefix */ 45 PetscCall(PetscStrcmp(STR(keynode), "<<", &isMergeKey)); 46 if (isMergeKey) { 47 if (valnode->type == YAML_SEQUENCE_NODE) { 48 for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) { 49 yaml_node_t *itemnode = yaml_document_get_node(doc, *item); 50 PetscCheck(itemnode,comm, PETSC_ERR_LIB, "Corrupt YAML document"); 51 PetscCheck(itemnode->type == YAML_MAPPING_NODE,comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping"); 52 PetscCall(PetscParseLayerYAML(options, doc, itemnode)); 53 } 54 } else if (valnode->type == YAML_MAPPING_NODE) { 55 PetscCall(PetscParseLayerYAML(options, doc, valnode)); 56 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected sequence or mapping"); 57 continue; /* to next pair */ 58 } 59 60 /* "$$*" are treated as dummy keys, we use them for !include tags and to define anchors */ 61 PetscCall(PetscStrbeginswith(STR(keynode), "$$", &isDummyKey)); 62 if (isDummyKey) { 63 PetscCall(PetscStrendswith(TAG(valnode), "!include", &isIncludeTag)); 64 if (isIncludeTag) { /* TODO: add proper support relative paths */ 65 PetscCall(PetscOptionsInsertFileYAML(comm, options, STR(valnode), PETSC_TRUE)); 66 } 67 continue; /* to next pair */ 68 } 69 70 if (valnode->type == YAML_SCALAR_NODE) { 71 PetscCall(PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode))); 72 PetscCall(PetscOptionsSetValue(options, name, STR(valnode))); 73 74 } else if (valnode->type == YAML_SEQUENCE_NODE) { 75 PetscSegBuffer seg; 76 char *buf, *strlist; 77 PetscBool addSep = PETSC_FALSE; 78 79 PetscCall(PetscSegBufferCreate(sizeof(char), PETSC_MAX_PATH_LEN, &seg)); 80 for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) { 81 yaml_node_t *itemnode = yaml_document_get_node(doc, *item); 82 const char *itemstr = NULL; 83 size_t itemlen; 84 85 PetscCheck(itemnode,comm, PETSC_ERR_LIB, "Corrupt YAML document"); 86 87 if (itemnode->type == YAML_SCALAR_NODE) { 88 itemstr = STR(itemnode); 89 90 } else if (itemnode->type == YAML_MAPPING_NODE) { 91 yaml_node_pair_t *kvn = itemnode->data.mapping.pairs.start; 92 yaml_node_pair_t *top = itemnode->data.mapping.pairs.top; 93 94 PetscCheck(top - kvn <= 1,comm, PETSC_ERR_SUP, "Unsupported YAML node value: expected a single key:value pair"); 95 if (top - kvn > 0) { 96 yaml_node_t *kn = yaml_document_get_node(doc, kvn->key); 97 yaml_node_t *vn = yaml_document_get_node(doc, kvn->value); 98 99 PetscCheck(kn,comm, PETSC_ERR_LIB, "Corrupt YAML document"); 100 PetscCheck(vn,comm, PETSC_ERR_LIB, "Corrupt YAML document"); 101 PetscCheck(kn->type == YAML_SCALAR_NODE,comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar"); 102 103 PetscCall(PetscStrcmp(STR(kn), "<<", &isMergeKey)); 104 PetscCheck(!isMergeKey,comm, PETSC_ERR_SUP, "Unsupported YAML node value: merge key '<<' not supported here"); 105 106 PetscCall(PetscStrbeginswith(STR(kn), "$$", &isDummyKey)); 107 if (isDummyKey) continue; 108 itemstr = STR(kn); 109 } 110 111 PetscCall(PetscSNPrintf(prefix,sizeof(prefix), "%s_", STR(keynode))); 112 PetscCall(PetscOptionsPrefixPush(options, prefix)); 113 PetscCall(PetscParseLayerYAML(options, doc, itemnode)); 114 PetscCall(PetscOptionsPrefixPop(options)); 115 116 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar or mapping"); 117 118 PetscCall(PetscStrlen(itemstr, &itemlen)); 119 if (itemlen) { 120 if (addSep) { 121 PetscCall(PetscSegBufferGet(seg, 1, &buf)); 122 PetscCall(PetscArraycpy(buf, ",", 1)); 123 } 124 PetscCall(PetscSegBufferGet(seg, itemlen, &buf)); 125 PetscCall(PetscArraycpy(buf, itemstr, itemlen)); 126 addSep = PETSC_TRUE; 127 } 128 } 129 PetscCall(PetscSegBufferGet(seg, 1, &buf)); 130 PetscCall(PetscArrayzero(buf, 1)); 131 PetscCall(PetscSegBufferExtractAlloc(seg, &strlist)); 132 PetscCall(PetscSegBufferDestroy(&seg)); 133 134 PetscCall(PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode))); 135 PetscCall(PetscOptionsSetValue(options, name, strlist)); 136 PetscCall(PetscFree(strlist)); 137 138 } else if (valnode->type == YAML_MAPPING_NODE) { 139 PetscCall(PetscSNPrintf(prefix,sizeof(prefix), "%s_", STR(keynode))); 140 PetscCall(PetscOptionsPrefixPush(options, prefix)); 141 PetscCall(PetscParseLayerYAML(options, doc, valnode)); 142 PetscCall(PetscOptionsPrefixPop(options)); 143 144 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar, sequence or mapping"); 145 } 146 PetscFunctionReturn(0); 147 } 148 149 /*@C 150 PetscOptionsInsertStringYAML - Inserts YAML-formatted options into the database from a string 151 152 Logically Collective 153 154 Input Parameters: 155 + options - options database, use NULL for default global database 156 - in_str - YAML-formatted string options 157 158 Level: intermediate 159 160 .seealso: `PetscOptionsSetValue()`, `PetscOptionsView()`, `PetscOptionsHasName()`, `PetscOptionsGetInt()`, 161 `PetscOptionsGetReal()`, `PetscOptionsGetString()`, `PetscOptionsGetIntArray()`, `PetscOptionsBool()`, 162 `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`, 163 `PetscOptionsStringArray(),PetscOptionsRealArray()`, `PetscOptionsScalar()`, 164 `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`, 165 `PetscOptionsFList()`, `PetscOptionsEList()`, `PetscOptionsInsertFile()`, `PetscOptionsInsertFileYAML()` 166 @*/ 167 PetscErrorCode PetscOptionsInsertStringYAML(PetscOptions options,const char in_str[]) 168 { 169 MPI_Comm comm = PetscYAMLGetComm(); 170 yaml_parser_t parser; 171 yaml_document_t doc; 172 yaml_node_t *root; 173 int err; 174 175 PetscFunctionBegin; 176 if (!in_str) in_str = ""; 177 err = !yaml_parser_initialize(&parser); PetscCheck(!err,comm, PETSC_ERR_LIB, "YAML parser initialization error"); 178 yaml_parser_set_input_string(&parser, (const unsigned char *)in_str, strlen(in_str)); 179 do { 180 err = !yaml_parser_load(&parser, &doc); PetscCheck(!err,comm, PETSC_ERR_LIB, "YAML parser loading error"); 181 root = yaml_document_get_root_node(&doc); 182 if (root) { 183 PetscCall(PetscParseLayerYAML(options, &doc, root)); 184 } 185 yaml_document_delete(&doc); 186 } while (root); 187 yaml_parser_delete(&parser); 188 PetscFunctionReturn(0); 189 } 190 191 /*@C 192 PetscOptionsInsertFileYAML - Insert a YAML-formatted file in the options database 193 194 Collective 195 196 Input Parameters: 197 + comm - the processes that will share the options (usually PETSC_COMM_WORLD) 198 . options - options database, use NULL for default global database 199 . file - name of file 200 - require - if PETSC_TRUE will generate an error if the file does not exist 201 202 PETSc will generate an error condition that stops the program if a YAML error 203 is detected, hence the user should check that the YAML file is valid before 204 supplying it, for instance at http://www.yamllint.com/ . 205 206 Uses PetscOptionsInsertStringYAML(). 207 208 Level: intermediate 209 210 .seealso: `PetscOptionsSetValue()`, `PetscOptionsView()`, `PetscOptionsHasName()`, `PetscOptionsGetInt()`, 211 `PetscOptionsGetReal()`, `PetscOptionsGetString()`, `PetscOptionsGetIntArray()`, `PetscOptionsBool()`, 212 `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`, 213 `PetscOptionsStringArray(),PetscOptionsRealArray()`, `PetscOptionsScalar()`, 214 `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`, 215 `PetscOptionsFList()`, `PetscOptionsEList()`, `PetscOptionsInsertFile()`, `PetscOptionsInsertStringYAML()` 216 @*/ 217 PetscErrorCode PetscOptionsInsertFileYAML(MPI_Comm comm,PetscOptions options,const char file[],PetscBool require) 218 { 219 int yamlLength = -1; 220 char *yamlString = NULL; 221 MPI_Comm prev; 222 PetscMPIInt rank; 223 224 PetscFunctionBegin; 225 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 226 if (rank == 0) { 227 char fpath[PETSC_MAX_PATH_LEN]; 228 char fname[PETSC_MAX_PATH_LEN]; 229 FILE *fd; 230 size_t rd; 231 232 PetscCall(PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath))); 233 PetscCall(PetscFixFilename(fpath, fname)); 234 235 fd = fopen(fname, "r"); 236 if (fd) { 237 fseek(fd, 0, SEEK_END); 238 yamlLength = (int)ftell(fd); 239 fseek(fd, 0, SEEK_SET); 240 PetscCheck(yamlLength >= 0,PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to query size of YAML file: %s", fname); 241 PetscCall(PetscMalloc1(yamlLength+1, &yamlString)); 242 rd = fread(yamlString, 1, (size_t)yamlLength, fd); 243 PetscCheck(rd == (size_t)yamlLength,PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read entire YAML file: %s", fname); 244 yamlString[yamlLength] = 0; 245 fclose(fd); 246 } 247 } 248 249 PetscCallMPI(MPI_Bcast(&yamlLength, 1, MPI_INT, 0, comm)); 250 PetscCheck(!require || yamlLength >= 0,comm, PETSC_ERR_FILE_OPEN, "Unable to open YAML option file: %s", file); 251 if (yamlLength < 0) PetscFunctionReturn(0); 252 253 if (rank) PetscCall(PetscMalloc1(yamlLength+1, &yamlString)); 254 PetscCallMPI(MPI_Bcast(yamlString, yamlLength+1, MPI_CHAR, 0, comm)); 255 256 prev = PetscYAMLSetComm(comm); 257 PetscCall(PetscOptionsInsertStringYAML(options, yamlString)); 258 (void) PetscYAMLSetComm(prev); 259 260 PetscCall(PetscFree(yamlString)); 261 PetscFunctionReturn(0); 262 } 263 264 #if !defined(PETSC_HAVE_YAML) 265 266 /* 267 #if !defined(PETSC_HAVE_STRDUP) 268 #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1) 269 #endif 270 */ 271 272 /* Embed LibYAML in this compilation unit */ 273 #include <../src/sys/yaml/src/api.c> 274 #include <../src/sys/yaml/src/loader.c> 275 #include <../src/sys/yaml/src/parser.c> 276 #include <../src/sys/yaml/src/reader.c> 277 278 /* 279 Avoid compiler warnings like 280 scanner.c, line 3181: warning: integer conversion resulted in a change of sign 281 *(string.pointer++) = '\xC2'; 282 283 Once yaml fixes them, we can remove the pragmas 284 */ 285 #pragma GCC diagnostic push 286 #pragma GCC diagnostic ignored "-Wsign-conversion" 287 #include <../src/sys/yaml/src/scanner.c> 288 #pragma GCC diagnostic pop 289 290 /* Silence a few unused-function warnings */ 291 static PETSC_UNUSED void petsc_yaml_unused(void) 292 { 293 (void)yaml_parser_scan; 294 (void)yaml_document_get_node; 295 (void)yaml_parser_set_encoding; 296 (void)yaml_parser_set_input; 297 (void)yaml_parser_set_input_file; 298 } 299 300 #endif 301