/** * Explores the given dictionary to match the given word. */ static void explore_dic(int offset,unichar* word,int pos_word,Dictionary* d,SpellCheckConfig* cfg, Ustring* output,SpellCheckHypothesis* *list,int base,Ustring* inflected) { int original_offset=offset; int original_base=base; int final,n_transitions,inf_code; int z=save_output(output); int size_pairs=cfg->pairs->nbelems; offset=read_dictionary_state(d,offset,&final,&n_transitions,&inf_code); if (final) { if (word[pos_word]=='\0') { /* If we have a match */ deal_with_matches(d,inflected->str,inf_code,output,cfg,base,list); } base=output->len; } /* If we are at the end of the token, then we stop */ if (word[pos_word]=='\0') { return; } unsigned int l2=inflected->len; unichar c; int dest_offset; for (int i=0;i<n_transitions;i++) { restore_output(z,output); offset=read_dictionary_transition(d,offset,&c,&dest_offset,output); /* For backup_output, see comment below */ int backup_output=save_output(output); if (c==word[pos_word] || word[pos_word]==u_toupper(c)) { u_strcat(inflected,c); explore_dic(dest_offset,word,pos_word+1,d,cfg,output,list,base,inflected); } else { /* We deal with the SP_SWAP case, made of 2 SP_CHANGE_XXX */ if (cfg->current_errors!=cfg->max_errors && cfg->current_SP_SWAP!=cfg->max_SP_SWAP && is_letter_swap(cfg,word,pos_word,inflected,c)) { /* We don't modify the number of errors since we override an existing * SP_CHANGE_XXX one */ cfg->current_SP_SWAP++; /* We override the previous change */ int a=cfg->pairs->tab[cfg->pairs->nbelems-2]; int b=cfg->pairs->tab[cfg->pairs->nbelems-1]; cfg->pairs->tab[cfg->pairs->nbelems-2]=pos_word-1; cfg->pairs->tab[cfg->pairs->nbelems-1]=SP_SWAP_DEFAULT; u_strcat(inflected,c); explore_dic(dest_offset,word,pos_word+1,d,cfg,output,list,base,inflected); cfg->pairs->tab[cfg->pairs->nbelems-2]=a; cfg->pairs->tab[cfg->pairs->nbelems-1]=b; cfg->current_SP_SWAP--; } else /* We deal with the SP_CHANGE case */ if (cfg->current_errors!=cfg->max_errors && cfg->current_SP_CHANGE!=cfg->max_SP_CHANGE /* We want letters, not spaces or anything else */ && is_letter(c,NULL) /* We do not allow the replacement of a lowercase letter by an uppercase * letter at the beginning of the word like Niserable, unless the whole word * is in uppercase or the letter is the same, module the case */ && (cfg->allow_uppercase_initial || pos_word>0 || (!is_upper(word[0],NULL) || is_upper(word[1],NULL) || word[0]==u_toupper(c)))) { cfg->current_errors++; cfg->current_SP_CHANGE++; /* Now we test all possible kinds of change */ vector_int_add(cfg->pairs,pos_word); u_strcat(inflected,c); /* We always add the default case */ vector_int_add(cfg->pairs,SP_CHANGE_DEFAULT); int n_elem=cfg->pairs->nbelems; explore_dic(dest_offset,word,pos_word+1,d,cfg,output,list,base,inflected); /* Then we test the accent case */ if (u_deaccentuate(c)==u_deaccentuate(word[pos_word])) { /* After a call to explore_dic, we must restore the output. * But, when dealing with SP_CHANGE_XXX ops, we must restore the * output including the output associated to the current transition, * which is why we don't use z (output before the current transition) * but backup_output */ restore_output(backup_output,output); cfg->pairs->nbelems=n_elem; cfg->pairs->tab[cfg->pairs->nbelems-1]=SP_CHANGE_DIACRITIC; explore_dic(dest_offset,word,pos_word+1,d,cfg,output,list,base,inflected); } /* And the case variations */ if (u_tolower(c)==u_tolower(word[pos_word])) { restore_output(backup_output,output); cfg->pairs->nbelems=n_elem; cfg->pairs->tab[cfg->pairs->nbelems-1]=SP_CHANGE_CASE; explore_dic(dest_offset,word,pos_word+1,d,cfg,output,list,base,inflected); } /* And finally the position on keyboard */ if (areCloseOnKeyboard(c,word[pos_word],cfg->keyboard)) { restore_output(backup_output,output); cfg->pairs->nbelems=n_elem; cfg->pairs->tab[cfg->pairs->nbelems-1]=SP_CHANGE_KEYBOARD; explore_dic(dest_offset,word,pos_word+1,d,cfg,output,list,base,inflected); } cfg->pairs->nbelems=size_pairs; cfg->current_errors--; cfg->current_SP_CHANGE--; /* End of the SP_CHANGE case */ } } restore_output(backup_output,output); truncate(inflected,l2); /* Now we deal with the SP_SUPPR case */ if (cfg->current_errors!=cfg->max_errors && cfg->current_SP_SUPPR!=cfg->max_SP_SUPPR /* We want letters, not spaces or anything else */ && is_letter(c,NULL)) { cfg->current_errors++; cfg->current_SP_SUPPR++; vector_int_add(cfg->pairs,pos_word); if (pos_word>=1 && c==word[pos_word-1]) { vector_int_add(cfg->pairs,SP_SUPPR_DOUBLE); } else { vector_int_add(cfg->pairs,SP_SUPPR_DEFAULT); } u_strcat(inflected,c); explore_dic(dest_offset,word,pos_word,d,cfg,output,list,original_base,inflected); truncate(inflected,l2); cfg->pairs->nbelems=size_pairs; cfg->current_errors--; cfg->current_SP_SUPPR--; } } restore_output(z,output); /* Finally, we deal with the SP_INSERT case, by calling again the current * function with the same parameters, except pos_word that will be increased of 1 */ if (cfg->current_errors!=cfg->max_errors && cfg->current_SP_INSERT!=cfg->max_SP_INSERT /* We want letters, not spaces or anything else */ && is_letter(word[pos_word],NULL) /* We do not allow the insertion of a capital letter at the beginning of * the word like Astreet, unless the whole word is in uppercase like ASTREET */ && (cfg->allow_uppercase_initial || pos_word>0 || (!is_upper(word[0],NULL) || is_upper(word[1],NULL)))) { cfg->current_errors++; cfg->current_SP_INSERT++; vector_int_add(cfg->pairs,pos_word); if (pos_word>=1 && word[pos_word]==word[pos_word-1]) { vector_int_add(cfg->pairs,SP_INSERT_DOUBLE); } else { vector_int_add(cfg->pairs,SP_INSERT_DEFAULT); } explore_dic(original_offset,word,pos_word+1,d,cfg,output,list,original_base,inflected); truncate(inflected,l2); cfg->pairs->nbelems=size_pairs; cfg->current_errors--; cfg->current_SP_INSERT--; } /* Finally, we restore the output as it was when we enter the function */ restore_output(z,output); }
int main(int argc, char** argv) { int ch; boolean priority_mode = TRUE; boolean test_mode = FALSE; boolean disable_partial_commit = FALSE; boolean full_commit_check = FALSE; boolean break_priority = FALSE; int break_priority_node = -1; boolean disable_hook = FALSE; char *commit_comment = NULL; /* this is needed before calling certain glib functions */ g_type_init(); //grab inputs while ((ch = getopt(argc, argv, "xdpthsecoafb:rlC:")) != -1) { switch (ch) { case 'x': g_old_print_output = TRUE; break; case 'd': g_debug = TRUE; break; case 'h': usage(); exit(0); break; case 'p': priority_mode = FALSE; break; case 't': test_mode = TRUE; break; case 's': g_dump_trans = TRUE; break; case 'e': g_display_error_node = TRUE; break; case 'c': g_coverage = TRUE; break; case 'o': disable_partial_commit = TRUE; break; case 'a': g_dump_actions = TRUE; break; case 'f': full_commit_check = TRUE; break; case 'b': break_priority_node = strtoul(optarg,NULL,10); break_priority = TRUE; break; case 'r': disable_hook = TRUE; break; case 'C': commit_comment = strdup(optarg); break; case 'l': release_config_lock(); break; default: usage(); exit(0); } } //can also be set via environment variable if (getenv("VYATTA_OUTPUT_ERROR_LOCATION") != NULL) { g_print_error_location_all = TRUE; } if (disable_hook == FALSE) { execute_hook(PRE_COMMIT_HOOK_DIR,commit_comment); } initialize_output("Commit"); init_paths(TRUE); d_dplog("commit2: starting up"); if (get_config_lock() != 0) { fprintf(out_stream, "Configuration system temporarily locked " "due to another commit in progress\n"); exit(1); } //get local session data plus configuration data GNode *config_data = common_get_local_session_data(); if (g_node_n_children(config_data) == 0) { common_commit_clean_temp_config(NULL, test_mode); fprintf(out_stream, "No configuration changes to commit\n"); return 0; } GNode *orig_node_tree = g_node_copy(config_data); // Get collection of transactions, i.e. trans nodes that have been activated. GNode *trans_coll = get_transactions(config_data, priority_mode); if (trans_coll == NULL) { printf("commit2: transactions collection is empty, exiting\n"); exit(0); } if (g_debug == TRUE || g_dump_trans == TRUE) { if (g_dump_trans == TRUE) { fprintf(out_stream,"Dumping transactions\n"); syslog(LOG_DEBUG,"Dumping transactions"); } //iterate over config_data and dump... g_node_traverse(trans_coll, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)dump_func, (gpointer)NULL); if (g_dump_trans == TRUE) { exit(0); } } set_in_commit(TRUE); GNode *trans_child_node = (GNode*)g_node_first_child(trans_coll); if (trans_child_node == NULL) { printf("commit2: No child nodes to process, exiting\n"); exit(0); } //open the changes file and clear FILE *fp_changes = fopen(COMMIT_CHANGES_FILE,"w"); if (fp_changes == NULL) { cond_plog(true, LOG_ERR, "commit2: Cannot access changes file, exiting"); exit(0); } GSList *completed_root_node_coll = NULL; GSList *nodes_visited_coll = NULL; int errors = 0; int i = 0; do { boolean success = FALSE; d_dplog("commit2: Starting new transaction processing pass on root: [%s]", ((trans_child_node && trans_child_node->data && ((struct VyattaNode*)(trans_child_node->data))->_data._name) ? ((struct VyattaNode*)(trans_child_node->data))->_data._name : "")); if (break_priority) { gpointer gp = ((GNode*)trans_child_node)->data; long p = (long) ((struct VyattaNode*)gp)->_config._priority; if (p >= break_priority_node) { g_dump_trans = TRUE; g_node_traverse(trans_child_node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)dump_func, (gpointer)NULL); g_dump_trans = FALSE; fprintf(out_stream,"Press any key to commit...\n"); // Wait for single character char input = getchar(); input = input; //to fix stupid compilier warning } } //complete() now requires a undisturbed copy of the trans_child_node tree GNode *comp_cp_node = g_node_copy(trans_child_node); if (g_dump_actions == TRUE) { fprintf(out_stream,"\n"); //add an extra line here } //on each priority node now execute actions nodes_visited_coll = NULL; if (validate_configuration(trans_child_node, full_commit_check, &nodes_visited_coll) == TRUE && (success = process_priority_node(trans_child_node)) == TRUE) { //this below copies the node directory from the local to active location //if this is true root skip if (trans_child_node != NULL && trans_child_node->data != NULL && strcmp(((struct VyattaNode*)(trans_child_node->data)) ->_data._path, "/") == 0) { //no op, need better way to define true root } else { if (disable_partial_commit == FALSE && g_dump_actions == FALSE) { completed_root_node_coll = g_slist_append(completed_root_node_coll, comp_cp_node); } } } if (g_dump_actions == TRUE) { success = TRUE; //FORCE SUCCESS ON DISPLAY MODE OF ACTIONS } if (success == FALSE) { errors |= 1; d_dplog("commit2: Failed in processing node"); } else { errors |= 2; } //now update the changes file update_change_file(fp_changes,nodes_visited_coll); fflush(fp_changes); ++i; } while ((trans_child_node = (GNode*) g_node_nth_child((GNode*) trans_coll, (guint) i)) != NULL); if (errors == 2) { /* * Need to add to the following func below to clean up dangling .wh. files */ if (g_dump_actions == FALSE) { common_commit_copy_to_live_config(orig_node_tree, TRUE, test_mode); common_commit_clean_temp_config(orig_node_tree, test_mode); } d_dplog("commit2: successful commit, now cleaning up temp directories"); } else { fprintf(out_stream,"Commit failed\n"); complete(completed_root_node_coll, test_mode); } set_in_commit(FALSE); d_dplog("DONE"); if (fp_changes != NULL) { fclose(fp_changes); } restore_output(); if (disable_hook == FALSE) { if (errors == 2) { setenv(ENV_COMMIT_STATUS,"SUCCESS",1); } else if (errors == 3) { setenv(ENV_COMMIT_STATUS,"PARTIAL",1); } else { setenv(ENV_COMMIT_STATUS,"FAILURE",1); } execute_hook(POST_COMMIT_HOOK_DIR,commit_comment); unsetenv(ENV_COMMIT_STATUS); } //remove tmp changes file as all the work is now done unlink(COMMIT_CHANGES_FILE); exit (errors == 2 ? 0 : 1); }