/* Gnome BibTeX bibfiles.C * Alejandro Aguilar Sierra * Felipe Bergo * * This program is free software; you can redistribute it and'or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. */ // File operations #include #include #include #include #include #include #include "gbib.h" using namespace std; bool use_braces = true; int ispseudoalpha(char c); int lt_space(char c); // Read everything inside delimiters char *read_field(FILE *inf, char &c) { int i=0, braceCount = 0, quoteCount = 0, parenCount=0; bool wasslash = false, is_brace, is_quote, is_paren, no_delim; static char buf[2000]; buf[0] = '\0'; // Eat spaces while (lt_space(c) && !feof(inf)) c=getc(inf); is_brace = bool(c == '{'); is_quote = bool(c == '"'); is_paren = bool(c == '('); no_delim = bool(!is_brace && !is_quote && !is_paren); while (!feof(inf)) { if ((c==',' || c=='\n') && (no_delim || (is_brace && braceCount==0) || (is_quote && quoteCount==0) || (is_paren && parenCount==0) )) { buf[i] = 0; break; } if (c=='{') braceCount++; if (c=='}') { braceCount--; if ((braceCount<0)||(braceCount==0 && is_brace)) { buf[i] = c; if (braceCount) { braceCount++; buf[i] = 0; } else c=' '; break; } } if (c=='"' && !wasslash && is_quote) { quoteCount = 1 - quoteCount; if (quoteCount==0) { buf[i] = '"'; break; } } if (c=='(') parenCount++; if (c==')') { parenCount--; if ((parenCount<0)||(parenCount==0 && is_paren)) { buf[i] = c; if (parenCount) { parenCount++; buf[i] = 0; } else c=' '; break; } } buf[i++] = c; // Size Limit if (i > 1998) break; wasslash = (c == '\\'); c = getc(inf); } // main while // Broken file if (feof(inf) && braceCount>0) return 0; if (braceCount<0) { buf[i] = 0; cerr << _("More } than {, possible error ") << &buf[0] << "\n"; i--; ungetc('}',inf); } if (parenCount<0) { buf[i] = 0; cerr << _("More ) than (, possible error ") << &buf[0] << "\n"; i--; ungetc(')',inf); } // Eat right spaces while (lt_space(buf[i]) && i>0) i--; if ((is_brace && buf[0]=='{' && buf[i]=='}') || (is_quote && buf[0]=='"' && buf[i]=='"') || (is_paren && buf[0]=='(' && buf[i]==')') ) { buf[i]=0; return &buf[1]; } buf[i+1]=0; return &buf[0]; } // Read the whole entry BibEntry *read_entry(FILE *inf) { int i; char c, name[100], key[100], fieldname[100]; BibEntry *entry = 0; Delimiter delim=BRACE; char ld,rd; // Eat space do c = getc(inf); while (lt_space(c) && !feof(inf)); // Eat everything before the @ while (c!='@' && !feof(inf)) { // skip comments if (c=='%') while (!feof(inf) && getc(inf)!='\n'); c = getc(inf); } // This is not a BibTeX entry! if (c != '@') return 0; // Eat everything before the name do c = getc(inf); while(!ispseudoalpha(c) && !feof(inf)); // read the entry type name for (i=0; ispseudoalpha(c) && !feof(inf); i++) { name[i] = c; c = getc(inf); } name[i] = 0; // Check that the name exist, otherwise it's a special case if (!getBibEntryDef(name)) { if (isCommand(name)) { entry = new BibEntry(name, 0); char *s = read_field(inf, c); if (s) entry->setField(0, s); return entry; } else { // A vanilla entrydef should not have required fields new_entrydef(name,"", ""); } } delim=BRACE; // Eat everything before the key while((c==' ' || c=='{' || c=='(' || c=='\n' || c=='\t' || c=='\r') && !feof(inf)) { c=getc(inf); if (c=='{') delim=BRACE; if (c=='"') delim=QUOTE; if (c=='(') delim=PAREN; } switch(delim) { case BRACE: ld='{'; rd='}'; break; case QUOTE: ld='"'; rd='"'; break; case PAREN: ld='('; rd=')'; break; } for (i=0; (c!=',' && !feof(inf)); i++) { key[i] = c; c = getc(inf); } key[i] = 0; if (feof(inf)) return 0; entry = new BibEntry(name, key); // cerr << "name=" << name << endl; // cerr << "key=" << key << endl; // Read fields do { // Eat any non-alpha, all field names start with alpha do { c = getc(inf); if ((c == '{') || (c == '"') || (c == '(')) { cerr << __FILE__ << ':' << __LINE__ << " Expecting fieldname here, got " << c << endl << " (fieldnames start with one of a-z or A-z and not '" << c << "')!" << endl << "in entry " << name << endl; delete entry; stringstream errMsg; errMsg << "fieldname expected, got '" << c << "' in entry '" << name << "'" << ends; throw runtime_error(errMsg.str()); } } while (!isalpha(c) && c!=rd && !feof(inf)); for (i=0; ispseudoalpha(c) && !feof(inf); i++) { fieldname[i] = tolower(c); c = getc(inf); } fieldname[i] = 0; while (lt_space(c) && !feof(inf)) c = getc(inf); if (c=='=') { c=getc(inf); char *s = read_field(inf, c); if (s) { entry->setField(fieldname, s); } } } while (c != rd && !feof(inf)); return entry; } static bool is_simple(char *s) { int i = 0; bool alpha = false, num = false; while (*s >= ' ') { if (!isalnum(*s)) { return 0; } if (isalpha(*s)) { alpha = true; } else { num = true; } i++; s++; } return ((num && !alpha) || (!num && alpha && i<4)) ? 1: 0; } void print_entry(FILE *fp, BibEntry *e) { int i; char ld = '{', rd = '}'; if (!use_braces) { rd = ld = '\"'; } if (e->isSpecial()) { fprintf(fp, "@%s{\n", e->getKey()); fprintf(fp, "%s\n}\n\n", e->getField(0)); return; } fprintf(fp, "@%s{%s,\n", e->getEntryType(), e->getKey()); for (i=0; i < e-> getNoFields(); i++) { if (e->getField(i)[0] >= ' ') { // Commenting this block results on every entry has delimiters, // which doesn't hurts. -- AAS /* if (is_simple(e->getField(i))) fprintf(fp, "\t%s = %s", e->getFieldName(i), e->getField(i)); else */ fprintf(fp, "\t%s = %c%s%c", e->getFieldName(i), ld, e->getField(i), rd); if (i < e->getNoFields()-1) fprintf(fp, ",\n"); } } fprintf(fp, "\n}\n\n"); } void print_commands(FILE *fp, BibentryTable *table) { for (int i=0; i < table->commandSize(); i++) { pair_strings p = table->getCommand(i); fprintf(fp, "@%s{%s}\n\n", p.first.c_str(), p.second.c_str()); } } // Finally the BibentryTable file methods int BibentryTable::readBibfile(char *name) { BibEntry *bib; FILE *inf = fopen(name, "r"); if (!inf) return -1; if (inf) { try { while ((bib=read_entry(inf))) { if (bib->isSpecial()) { new_command(bib->getKey(), bib->getField(0)); } else new_entry(*bib); delete bib; } } catch(runtime_error e) { fclose(inf); cerr << __FILE__ << ':' << __LINE__ << " Caught exception" << endl << " " << e.what() << endl << "while reading from file '" << name << "'." << endl << "Closing file '" << name << "' and rethrowing exception." << endl; throw e; } } string aux = name; dbname = ""; for (unsigned int i = aux.rfind('/')+1; i < aux.rfind('.'); i++) { dbname += name[i]; } return 0; } int BibentryTable::writeBibfile(const char *name) { int i = 0; BibEntry *bib; FILE *outf = fopen(name, "w"); if (!outf) return -1; print_commands(outf, this); while ((bib=get_entry(i++))) { print_entry(outf, bib); } fclose(outf); return 0; } int BibentryTable::writeBibfile2(char *name) { int i = 0; BibEntry *bib; FILE *outf = fopen(name, "w"); if (!outf) return -1; print_commands(outf, this); while ((bib=get_entry(i++))) { print_entry(outf, bib); } fclose(outf); return 0; } int ispseudoalpha(char c) { if (isalpha(c)) return 1; return ((c=='-')||(c=='_')); } // avoid recognizing characters >127 (negative when c is signed) // as control cahrs int lt_space(char c) { return( (c>=0) && (c<=' ') ); }