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