yang - add deviate delete statement
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 29dea1e..8816f07 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -588,6 +588,9 @@
         retval = &(*((struct type_deviation *)node)->trg_must)[(*((struct type_deviation *)node)->trg_must_size)++];
         memset(retval, 0, sizeof *retval);
         break;
+    case DELETE_KEYWORD:
+        retval = &((struct type_deviation *)node)->deviate->must[((struct type_deviation *)node)->deviate->must_size++];
+        break;
     }
     retval->expr = transform_schema2json(module, value, line);
     if (!retval->expr || lyxp_syntax_check(retval->expr, line)) {
@@ -1748,6 +1751,23 @@
         }
     }
 
+    if (dev->deviate->mod == LY_DEVIATE_DEL) {
+        /* check values */
+        if (*stritem != dev->deviate->units) {
+            LOGVAL(LYE_INARG, line, LY_VLOG_NONE, NULL, dev->deviate->units, "units");
+            LOGVAL(LYE_SPEC, 0, 0, NULL, "Value differs from the target being deleted.");
+            goto error;
+        }
+        /* remove current units value of the target */
+        lydict_remove(ctx, *stritem);
+    } else { /* add (already checked) and replace */
+        /* remove current units value of the target ... */
+        lydict_remove(ctx, *stritem);
+
+        /* ... and replace it with the value specified in deviation */
+        *stritem = lydict_insert(ctx, dev->deviate->units, 0);
+    }
+
     return EXIT_SUCCESS;
 
 error:
@@ -1797,11 +1817,13 @@
         *dev->trg_must = dev->deviate->must;
         dev->deviate->must = &((*dev->trg_must)[*dev->trg_must_size]);
         dev->deviate->must_size = c_must;
-    }
-
-    if (!dev->deviate->must) {
-        LOGMEM;
-        goto error;
+    } else {
+        /* LY_DEVIATE_DEL */
+        dev->deviate->must = calloc(c_must, sizeof *dev->deviate->must);
+        if (!dev->deviate->must) {
+            LOGMEM;
+            goto error;
+        }
     }
 
     return EXIT_SUCCESS;
@@ -1835,6 +1857,13 @@
         dev->deviate->unique = &list->unique[list->unique_size];
         dev->deviate->unique_size = c_uniq;
         memset(dev->deviate->unique, 0, c_uniq * sizeof *dev->deviate->unique);
+    } else {
+        /* LY_DEVIATE_DEL */
+        dev->deviate->unique = calloc(c_uniq, sizeof *dev->deviate->unique);
+        if (!dev->deviate->unique) {
+            LOGMEM;
+            goto error;
+        }
     }
 
     return EXIT_SUCCESS;
@@ -1876,12 +1905,20 @@
             LOGVAL(LYE_INARG, line, LY_VLOG_NONE, NULL, dev->deviate->dflt, "default");
             goto error;
         }
-        /* add (already checked) and replace */
-        choice->dflt = node;
-        if (!choice->dflt) {
-            /* default branch not found */
-            LOGVAL(LYE_INARG, line, LY_VLOG_NONE, NULL, dev->deviate->dflt, "default");
-            goto error;
+        if (dev->deviate->mod == LY_DEVIATE_DEL) {
+            if (!choice->dflt || (choice->dflt != node)) {
+                LOGVAL(LYE_INARG, line, LY_VLOG_NONE, NULL, dev->deviate->dflt, "default");
+                LOGVAL(LYE_SPEC, 0, 0, NULL, "Value differs from the target being deleted.");
+                goto error;
+            }
+            choice->dflt = NULL;
+        } else { /* add (already checked) and replace */
+            choice->dflt = node;
+            if (!choice->dflt) {
+                /* default branch not found */
+                LOGVAL(LYE_INARG, line, LY_VLOG_NONE, NULL, dev->deviate->dflt, "default");
+                goto error;
+            }
         }
     } else if (dev->target->nodetype == LYS_LEAF) {
         leaf = (struct lys_node_leaf *)dev->target;
@@ -1894,13 +1931,22 @@
                 goto error;
             }
         }
+        if (dev->deviate->mod == LY_DEVIATE_DEL) {
+            if (!leaf->dflt || (leaf->dflt != dev->deviate->dflt)) {
+                LOGVAL(LYE_INARG, line, LY_VLOG_NONE, NULL, dev->deviate->dflt, "default");
+                LOGVAL(LYE_SPEC, 0, 0, NULL, "Value differs from the target being deleted.");
+                goto error;
+            }
+            /* remove value */
+            lydict_remove(ctx, leaf->dflt);
+            leaf->dflt = NULL;
+        } else { /* add (already checked) and replace */
+            /* remove value */
+            lydict_remove(ctx, leaf->dflt);
 
-        /* add (already checked) and replace */
-        /* remove value */
-        lydict_remove(ctx, leaf->dflt);
-
-        /* set new value */
-        leaf->dflt = lydict_insert(ctx, dev->deviate->dflt, 0);
+            /* set new value */
+            leaf->dflt = lydict_insert(ctx, dev->deviate->dflt, 0);
+        }
     } else {
         /* invalid target for default value */
         LOGVAL(LYE_INSTMT, line, LY_VLOG_NONE, NULL, "default");
@@ -2029,3 +2075,113 @@
 error:
     return EXIT_FAILURE;
 }
+
+int
+yang_check_deviate_must(struct ly_ctx *ctx, struct type_deviation *dev, int line)
+{
+    int i;
+
+    /* find must to delete, we are ok with just matching conditions */
+    for (i = 0; i < *dev->trg_must_size; i++) {
+        if (ly_strequal(dev->deviate->must[dev->deviate->must_size - 1].expr, (*dev->trg_must)[i].expr, 1)) {
+            /* we have a match, free the must structure ... */
+            lys_restr_free(ctx, &((*dev->trg_must)[i]));
+            /* ... and maintain the array */
+            (*dev->trg_must_size)--;
+            if (i != *dev->trg_must_size) {
+                (*dev->trg_must)[i].expr = (*dev->trg_must)[*dev->trg_must_size].expr;
+                (*dev->trg_must)[i].dsc = (*dev->trg_must)[*dev->trg_must_size].dsc;
+                (*dev->trg_must)[i].ref = (*dev->trg_must)[*dev->trg_must_size].ref;
+                (*dev->trg_must)[i].eapptag = (*dev->trg_must)[*dev->trg_must_size].eapptag;
+                (*dev->trg_must)[i].emsg = (*dev->trg_must)[*dev->trg_must_size].emsg;
+            }
+            if (!(*dev->trg_must_size)) {
+                free(*dev->trg_must);
+                *dev->trg_must = NULL;
+            } else {
+                (*dev->trg_must)[*dev->trg_must_size].expr = NULL;
+                (*dev->trg_must)[*dev->trg_must_size].dsc = NULL;
+                (*dev->trg_must)[*dev->trg_must_size].ref = NULL;
+                (*dev->trg_must)[*dev->trg_must_size].eapptag = NULL;
+                (*dev->trg_must)[*dev->trg_must_size].emsg = NULL;
+            }
+
+            i = -1; /* set match flag */
+            break;
+        }
+    }
+    if (i != -1) {
+        /* no match found */
+        LOGVAL(LYE_INARG, line, LY_VLOG_NONE, NULL, dev->deviate->must[dev->deviate->must_size - 1].expr, "must");
+        LOGVAL(LYE_SPEC, 0, 0, NULL, "Value does not match any must from the target.");
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
+
+int
+yang_check_deviate_unique(struct lys_module *module, struct type_deviation *dev, char *value, int line)
+{
+    struct lys_node_list *list;
+    int i, j;
+
+    list = (struct lys_node_list *)dev->target;
+    if (yang_fill_unique(module, list, &dev->deviate->unique[dev->deviate->unique_size], value, NULL, line)) {
+        dev->deviate->unique_size++;
+        goto error;
+    }
+
+    /* find unique structures to delete */
+    for (i = 0; i < list->unique_size; i++) {
+        if (list->unique[i].expr_size != dev->deviate->unique[dev->deviate->unique_size].expr_size) {
+            continue;
+        }
+
+        for (j = 0; j < dev->deviate->unique[dev->deviate->unique_size].expr_size; j++) {
+            if (!ly_strequal(list->unique[i].expr[j], dev->deviate->unique[dev->deviate->unique_size].expr[j], 1)) {
+                break;
+            }
+        }
+
+        if (j == dev->deviate->unique[dev->deviate->unique_size].expr_size) {
+            /* we have a match, free the unique structure ... */
+            for (j = 0; j < list->unique[i].expr_size; j++) {
+                lydict_remove(module->ctx, list->unique[i].expr[j]);
+            }
+            free(list->unique[i].expr);
+            /* ... and maintain the array */
+            list->unique_size--;
+            if (i != list->unique_size) {
+                list->unique[i].expr_size = list->unique[list->unique_size].expr_size;
+                list->unique[i].expr = list->unique[list->unique_size].expr;
+            }
+
+            if (!list->unique_size) {
+                free(list->unique);
+                list->unique = NULL;
+            } else {
+                list->unique[list->unique_size].expr_size = 0;
+                list->unique[list->unique_size].expr = NULL;
+            }
+
+            i = -1; /* set match flag */
+            break;
+        }
+    }
+    dev->deviate->unique_size++;
+
+    if (i != -1) {
+        /* no match found */
+        LOGVAL(LYE_INARG, line, LY_VLOG_NONE, NULL, value, "unique");
+        LOGVAL(LYE_SPEC, 0, 0, NULL, "Value differs from the target being deleted.");
+        goto error;
+    }
+
+    free(value);
+    return EXIT_SUCCESS;
+
+error:
+    free(value);
+    return EXIT_FAILURE;
+}
diff --git a/src/parser_yang.h b/src/parser_yang.h
index a45ea43..54d3af2 100644
--- a/src/parser_yang.h
+++ b/src/parser_yang.h
@@ -263,4 +263,9 @@
  */
 int yang_read_deviate_minmax(struct type_deviation *dev, uint32_t value, int type, int line);
 
+int yang_check_deviate_must(struct ly_ctx *ctx, struct type_deviation *dev, int line);
+
+int yang_check_deviate_unique(struct lys_module *module, struct type_deviation *dev, char *value, int line);
+
+
 #endif /* LY_PARSER_YANG_H_ */
diff --git a/src/yang.y b/src/yang.y
index ce34bb3..aafb11e 100644
--- a/src/yang.y
+++ b/src/yang.y
@@ -197,6 +197,7 @@
 %type <nodes> notification_opt_stmt

 %type <nodes> deviation_opt_stmt

 %type <nodes> deviate_add_opt_stmt

+%type <nodes> deviate_delete_opt_stmt

 

 %destructor { free($$); } tmp_identifier_arg_str

 %destructor { if (read_all && $$.choice.s) { free($$.choice.s); } } choice_opt_stmt

@@ -2511,19 +2512,76 @@
                                               }

                                             }

 

-deviate_delete_stmt: DELETE_KEYWORD optsep deviate_delete_end;

+deviate_delete_stmt: DELETE_KEYWORD optsep { if (read_all && yang_read_deviate(actual, LY_DEVIATE_DEL, yylineno)) {

+                                               YYERROR;

+                                             }

+                                           }

+                     deviate_delete_end

 

 deviate_delete_end: ';'

-  |  '{' start_check

-         deviate_delete_opt_stmt  {free_check();}

+  |  '{' stmtsep

+         deviate_delete_opt_stmt

       '}' ;

 

-deviate_delete_opt_stmt: %empty 

-  |  deviate_delete_opt_stmt yychecked_1 units_stmt

-  |  deviate_delete_opt_stmt must_stmt

-  |  deviate_delete_opt_stmt unique_stmt

-  |  deviate_delete_opt_stmt yychecked_2 default_stmt

-  ;

+deviate_delete_opt_stmt: %empty { if (read_all) {

+                                    $$.deviation = actual;

+                                    actual_type = DELETE_KEYWORD;

+                                    if (size_arrays->node[size_arrays->next].must) {

+                                      if (yang_read_deviate_must(module->ctx, actual, size_arrays->node[size_arrays->next].must, yylineno)) {

+                                        YYERROR;

+                                      }

+                                    }

+                                    if (size_arrays->node[size_arrays->next].unique) {

+                                      if (yang_read_deviate_unique(module->ctx, actual, size_arrays->node[size_arrays->next].unique, yylineno)) {

+                                        YYERROR;

+                                      }

+                                    }

+                                    size_arrays->next++;

+                                  } else {

+                                    $$.index = size_arrays->size;

+                                    if (yang_add_elem(&size_arrays->node, &size_arrays->size)) {

+                                      LOGMEM;

+                                      YYERROR;

+                                    }

+                                  }

+                                }

+  |  deviate_delete_opt_stmt units_stmt { if (read_all) {

+                                            if (yang_read_deviate_units(module->ctx, $1.deviation, s, yylineno)) {

+                                              YYERROR;

+                                            }

+                                            s = NULL;

+                                            $$ = $1;

+                                          }

+                                        }

+  |  deviate_delete_opt_stmt must_stmt { if (read_all) {

+                                           if (yang_check_deviate_must(module->ctx, $1.deviation, yylineno)) {

+                                             YYERROR;

+                                           }

+                                           actual = $1.deviation;

+                                           actual_type = DELETE_KEYWORD;

+                                           $$ = $1;

+                                         } else {

+                                           size_arrays->node[$1.index].must++;

+                                         }

+                                       }

+  |  deviate_delete_opt_stmt unique_stmt { if (read_all) {

+                                             if (yang_check_deviate_unique(module, $1.deviation, s, yylineno)) {

+                                               YYERROR;

+                                             }

+                                             s = NULL;

+                                             $$ = $1;

+                                           } else {

+                                             size_arrays->node[$1.index].unique++;

+                                           }

+                                         }

+  |  deviate_delete_opt_stmt default_stmt { if (read_all) {

+                                              if (yang_read_deviate_default(module->ctx, $1.deviation, s, yylineno)) {

+                                                YYERROR;

+                                              }

+                                              s = NULL;

+                                              $$ = $1;

+                                            }

+                                          }

 

 deviate_replace_stmt: REPLACE_KEYWORD optsep deviate_replace_end;