#include #include #include "grubconffile.h" #include "grubconf.m" static CONFIG_FILE grubconf_file( "/boot/grub/menu.lst", help_nil,CONFIGF_MANAGED, "root","root",0600); enum { VIEWITEM_TITLE = VIEWITEM_LAST }; static const char *K_TITLE = "title"; const char *command_order[] = { "rootnoverify", "root", "kernel", "module", "initrd", "makeactive", "chainloader" }; int total_commands = sizeof(command_order)/sizeof(const char *); PUBLIC GRUB_VI_PARSER::GRUB_VI_PARSER() : VIEWITEMS_PARSER() { } PUBLIC void GRUB_VI_PARSER::addline(const char *line) { int type; int titlen = strlen(K_TITLE); if (is_comment(line)){ type = VIEWITEM_COMMENT; }else if (*(str_skip(line)) == 0){ type = VIEWITEM_EMPTY; }else if (strncmp(str_skip(line),K_TITLE,titlen) == 0){ type = VIEWITEM_TITLE; }else{ type = VIEWITEM_VARIABLE; } vi->add(new VIEWITEM(line,type)); } PUBLIC GRUBCONFFILE::GRUBCONFFILE() : vi(gvip), haschanged(0) { vi.read(grubconf_file); detect_ind_sep(); } PUBLIC GRUBCONFFILE::~GRUBCONFFILE() { if (haschanged) vi.write(grubconf_file,NULL); free(ind_globals); free(ind_titles); free(ind_scripts); free(sep_globals); free(sep_titles); free(sep_scripts); } PUBLIC int GRUBCONFFILE::count_titles() { return vi.getnb(VIEWITEM_TITLE); } PUBLIC const char *GRUBCONFFILE::locate(int title, const char *var) { VIEWITEM *it = this->locate_it(title,var); if (it != NULL){ const char *p = it->line.get(); if (*p == '\t' || *p == ' '){ p = str_skip(p); } p += strlen(var)+1; if (*p == '\t' || *p == ' '){ p = str_skip(p); } if (*p == '='){ p = str_skip(++p); } return p; } return NULL; } PRIVATE VIEWITEM *GRUBCONFFILE::locate_it(int title, const char *var) { int varlen = strlen(var); int start,end; if (title >= this->count_titles()){ return NULL; }else if (title == -1){ start = 0; end = vi.getnb(-1); }else{ start = vi.realpos(title,VIEWITEM_TITLE); if (start == -1){ start = 0; end = vi.getnb(-1); }else{ end = vi.realpos(title+1,VIEWITEM_TITLE); if (end == -1){ end = vi.getnb(-1); } } } for (int i = start; i < end; i++){ VIEWITEM *it = vi.getitem(i,-1); if (it->type == VIEWITEM_VARIABLE || it->type == VIEWITEM_TITLE){ const char *p = str_skip(it->line.get()); if (strncmp(p,var,varlen)==0){ p += varlen; if (*p == '\t' || *p == '\0' || *p == ' ' || *p == '='){ return it; } } } } return NULL; } PUBLIC void GRUBCONFFILE::update(int title, const char *var, const char *value) { const char *ind, *sep; haschanged = 1; if (title == -1){ ind = ind_globals; sep = sep_globals; }else if (strcmp(var,"title")==0){ ind = ind_titles; sep = sep_titles; }else if (strcmp(var,"chainloader")==0){ ind = ind_scripts; sep = " "; }else{ ind = ind_scripts; sep = sep_scripts; } if (*value == 0){ sep = ""; } VIEWITEM *it = this->locate_it(title,var); if (it != NULL){ it->line.setfromf("%s%s%s%s",ind,var,sep,value); }else{ int pos = this->find_insert_position(title,var); if (strcmp("title",var)==0){ it = new VIEWITEM("",VIEWITEM_TITLE); }else{ it = new VIEWITEM("",VIEWITEM_VARIABLE); } it->line.setfromf("%s%s%s%s",ind,var,sep,value); vi.insert(pos,it,-1); } } PRIVATE int GRUBCONFFILE::find_insert_position(int title, const char *var) { int nb = vi.getnb(-1); int last; if (title == -1){ int pos = 0; last = pos; for (; pos < nb; pos++){ VIEWITEM *it = vi.getitem(pos,-1); if (it->type == VIEWITEM_TITLE){ break; }else if (it->type == VIEWITEM_VARIABLE){ last = pos; } } }else{ if (title >= this->count_titles()){ return vi.getnb(-1); } int pos = vi.realpos(title,VIEWITEM_TITLE); last = pos; int total_commands = sizeof(command_order)/sizeof(const char *); // Find command position int order = -1; for (int j = 0; j < total_commands; j++){ if (strcmp(var,command_order[j])==0){ order = j; break; } } if (order == -1){ xconf_error(MSG_U(E_INVVAR,"Invalid variable!")); } pos++; // Jump the first title for (; pos < nb; pos++){ VIEWITEM *it = vi.getitem(pos,-1); if (it->type == VIEWITEM_TITLE){ break; }else if (it->type == VIEWITEM_VARIABLE){ bool found = true; int j = 0; const char *line = str_skip(it->line.get()); for (; j < total_commands; j++){ if (strncmp(line,command_order[j] ,strlen(command_order[j]))==0){ found = true; break; } } if (found == false){ break; }else if (j >= order){ break; }else{ last = pos; } } } } return last+1; } PUBLIC void GRUBCONFFILE::erase(int title, const char *var) { haschanged = 1; VIEWITEM *it = this->locate_it(title,var); if (it != NULL){ vi.remove_del(it); } } PRIVATE char *GRUBCONFFILE::extract_indent(VIEWITEM *it) { const char *start = it->line.get(); const char *end = start; while (*end == ' ' || *end == '\t') end++; int len = end-start; char *ret = (char *) malloc(len+1); memcpy(ret,start,len); ret[len] = 0; return ret; } PRIVATE char *GRUBCONFFILE::extract_separator(VIEWITEM *it) { const char *start = it->line.get(); while (*start == ' ' || *start == '\t') start++; while (*start != ' ' && *start != '\t' && *start != '=') start++; const char *end = start; bool first_equal = true; while (*end == ' ' || *end == '\t' || (*end == '=' && first_equal)){ if (*end == '=') first_equal = false; end++; } int len = end-start; char *ret = (char *) malloc(len+1); memcpy(ret,start,len); ret[len] = 0; return ret; } PRIVATE void GRUBCONFFILE::detect_ind_sep() { const char *ind_default = ""; const char *sep_default = " "; VIEWITEM *it; int nb = vi.getnb(-1); int i = 0; ind_globals = ind_titles = ind_scripts = NULL; sep_globals = sep_titles = sep_scripts = NULL; for (; i < nb; i++){ it = vi.getitem(i,-1); if (it->type == VIEWITEM_VARIABLE){ ind_globals = extract_indent(it); sep_globals = extract_separator(it); i++; break; }else if (it->type == VIEWITEM_TITLE){ break; } } for (; i < nb; i++){ it = vi.getitem(i,-1); if (it->type == VIEWITEM_TITLE){ ind_titles = extract_indent(it); sep_titles = extract_separator(it); i++; break; } } for (; i < nb; i++){ it = vi.getitem(i,-1); if (it->type == VIEWITEM_VARIABLE){ ind_scripts = extract_indent(it); sep_scripts = extract_separator(it); break; } } if (ind_globals == NULL){ ind_globals = strdup(ind_default); ind_titles = strdup(ind_default); ind_scripts = strdup(ind_default); sep_globals = strdup(sep_default); sep_titles = strdup(sep_default); sep_scripts = strdup(sep_default); }else if (ind_titles == NULL){ ind_titles = strdup(ind_default); ind_scripts = strdup(ind_default); sep_titles = strdup(sep_default); sep_scripts = strdup(sep_default); }else if (ind_scripts == NULL){ ind_scripts = strdup(ind_default); sep_scripts = strdup(sep_default); } } PUBLIC bool GRUBCONFFILE::check_script(int pos) { int cur = vi.realpos(pos,VIEWITEM_TITLE); if (cur == -1) return -1; int end = vi.realpos(pos+1,VIEWITEM_TITLE); if (end == -1) end = vi.getnb(-1); int order = -1; for (int i = cur+1; i < end; i++) { VIEWITEM *it = vi.getitem(i,-1); const char *line = str_skip(it->line.get()); if (it->type == VIEWITEM_VARIABLE){ int j = 0; bool found = false; for (; j < total_commands; j++){ if (strncmp(line,command_order[j] ,strlen(command_order[j]))==0){ found = true; while (j <= order && j+1 < total_commands && strncmp(line,command_order[j+1], strlen(command_order[j+1]))==0){ j++; } break; } } if (found == false || j <= order){ if (found == false){ xconf_error(MSG_U(E_COMMINV,"Unsupported command!")); }else if (j == order){ xconf_error(MSG_U(E_COMMDUP,"Duplicated command!")); }else{ xconf_error(MSG_U(E_COMMORD,"Command out of order!")); } return false; } order = j; } } return true; }