Changeset 28029 for trunk/base


Ignore:
Timestamp:
Aug 18, 2007, 3:59:59 PM (13 years ago)
Author:
sfiera@…
Message:

Incremental update on registry2

Also added ${prefix}/var/macports/registry to prefix.mtree for the future

Location:
trunk/base
Files:
4 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/base/doc/prefix.mtree.in

    r27892 r28029  
    243243            ..
    244244            receipts
     245        ..
     246            registry
    245247            ..
    246248        ..
  • trunk/base/src/cregistry/entry.c

    r27967 r28029  
    4949 * TODO: process the 'state' keyword in a more efficient way. I still believe
    5050 *       there could be benefits to be reaped in the future by allowing
    51  *       arbitrary values but at the same time it will always have very discrete
    52  *       values. These could be more efficiently dealt with as integers.
     51 *       arbitrary values; but at the same time the field will (or should)
     52 *       always have very discrete values. These could be more efficiently dealt
     53 *       with as integers.
    5354 *
    5455 * TODO: move the utility functions to util.h or something. Not important until
    5556 *       there are more types in the registry than entry, though.
     57 *
     58 * TODO: considering a "weak" flag in registry.files. The meaning of this would
     59 *       be "I wish for my version of this file to be activated when I am, but
     60 *       not to be deactivated when I am; nor should other ports be prevented
     61 *       from overwriting this file." This would be useful for files like e.g.
     62 *       perllocal.pod (or whatever it's called; the one that causes problems).
     63 *
     64 * TODO: expose a file's mtime attribute. This should be used during port
     65 *       deactivation to determine if any files have local modifications. If so,
     66 *       they should be moved aside instead of being removed when the port is
     67 *       deactivated/uninstalled.
    5668 */
    5769
     
    237249    sqlite3_stmt* stmt;
    238250    reg_entry* entry = NULL;
    239     char* query = "SELECT registry.ports.id FROM registry.ports "
    240         "WHERE name=? AND version=? AND revision=? AND variants=? AND epoch=?";
     251    char* query = "SELECT id FROM registry.ports WHERE name=? AND version=? "
     252        "AND revision=? AND variants=? AND epoch=?";
    241253    if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
    242254            && (sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC)
     
    288300    reg_registry* reg = entry->reg;
    289301    int result = 0;
    290     sqlite3_stmt* stmt;
    291     char* query = "DELETE FROM registry.ports WHERE id=?";
    292     if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
    293             && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
     302    sqlite3_stmt* ports;
     303    sqlite3_stmt* files;
     304    sqlite3_stmt* dependencies;
     305    char* ports_query = "DELETE FROM registry.ports WHERE id=?";
     306    char* files_query = "DELETE FROM registry.files WHERE id=?";
     307    char* dependencies_query = "DELETE FROM registry.dependencies WHERE id=?";
     308    if ((sqlite3_prepare(reg->db, ports_query, -1, &ports, NULL) == SQLITE_OK)
     309            && (sqlite3_bind_int64(ports, 1, entry->id) == SQLITE_OK)
     310            && (sqlite3_prepare(reg->db, files_query, -1, &files, NULL)
     311                == SQLITE_OK)
     312            && (sqlite3_bind_int64(files, 1, entry->id) == SQLITE_OK)
     313            && (sqlite3_prepare(reg->db, dependencies_query, -1, &dependencies,
     314                    NULL) == SQLITE_OK)
     315            && (sqlite3_bind_int64(dependencies, 1, entry->id) == SQLITE_OK)) {
    294316        int r;
    295317        do {
    296             r = sqlite3_step(stmt);
     318            r = sqlite3_step(ports);
    297319            switch (r) {
    298320                case SQLITE_DONE:
    299321                    if (sqlite3_changes(reg->db) > 0) {
    300                         result = 1;
     322                        do {
     323                            r = sqlite3_step(files);
     324                            switch (r) {
     325                                case SQLITE_DONE:
     326                                    do {
     327                                        r = sqlite3_step(dependencies);
     328                                        switch (r) {
     329                                            case SQLITE_DONE:
     330                                                result = 1;
     331                                                break;
     332                                            case SQLITE_BUSY:
     333                                                break;
     334                                            case SQLITE_ERROR:
     335                                                reg_sqlite_error(reg->db,
     336                                                        errPtr, NULL);
     337                                                break;
     338                                        }
     339                                    } while (r == SQLITE_BUSY);
     340                                    break;
     341                                case SQLITE_BUSY:
     342                                    break;
     343                                case SQLITE_ERROR:
     344                                    reg_sqlite_error(reg->db, errPtr, NULL);
     345                                    break;
     346                            }
     347                        } while (r == SQLITE_BUSY);
     348                        break;
    301349                    } else {
    302350                        errPtr->code = REG_INVALID;
     
    308356                    break;
    309357                case SQLITE_ERROR:
    310                     reg_sqlite_error(reg->db, errPtr, query);
     358                    reg_sqlite_error(reg->db, errPtr, NULL);
    311359                    break;
    312360            }
    313361        } while (r == SQLITE_BUSY);
    314362    } else {
    315         reg_sqlite_error(reg->db, errPtr, query);
    316     }
    317     sqlite3_finalize(stmt);
     363        reg_sqlite_error(reg->db, errPtr, NULL);
     364    }
     365    sqlite3_finalize(ports);
     366    sqlite3_finalize(files);
     367    sqlite3_finalize(dependencies);
    318368    return result;
    319369}
     
    433483    char* kwd = " WHERE ";
    434484    char* query;
    435     int query_len = 44;
    436     int query_space = 44;
     485    int query_len = 29;
     486    int query_space = 29;
    437487    int result;
    438488    /* get the strategy */
     
    442492    }
    443493    /* build the query */
    444     query = strdup("SELECT registry.ports.id FROM registry.ports");
     494    query = strdup("SELECT id FROM registry.ports");
    445495    for (i=0; i<key_count; i++) {
    446496        char* cond = sqlite3_mprintf(op, keys[i], vals[i]);
     
    460510 * in the filesystem. When the install mode is 'direct', this will be equivalent
    461511 * to `reg_entry_installed`.
    462  * @todo add more arguments (epoch, revision, variants), maybe
     512 *
     513 * Note that the name is a bit of a misnomer, since you can install a port with
     514 * installtype direct and it will still be in this list. It's really "imaged or
     515 * installed" but that's usually redundant and too long to type.
    463516 *
    464517 * @param [in] reg      registry object as created by `registry_open`
     
    469522 * @return              the number of entries if success; false if failure
    470523 */
    471 int reg_entry_imaged(reg_registry* reg, char* name, char* version,
    472         reg_entry*** entries, reg_error* errPtr) {
    473     char* format;
     524int reg_entry_imaged(reg_registry* reg, const char* name, const char* version,
     525        const char* revision, const char* variants, reg_entry*** entries,
     526        reg_error* errPtr) {
    474527    char* query;
    475528    int result;
    476     char* select = "SELECT registry.ports.id FROM registry.ports";
    477     if (name == NULL) {
    478         format = "%s WHERE (state='imaged' OR state='installed')";
    479     } else if (version == NULL) {
    480         format = "%s WHERE (state='imaged' OR state='installed') AND name='%q'";
    481     } else {
    482         format = "%s WHERE (state='imaged' OR state='installed') AND name='%q' "
    483             "AND version='%q'";
    484     }
    485     query = sqlite3_mprintf(format, select, name, version);
     529    char* empty = "";
     530    char* name_clause = empty;
     531    char* version_clause = empty;
     532    char* revision_clause = empty;
     533    char* variants_clause = empty;
     534    if (name != NULL) {
     535        name_clause = sqlite3_mprintf(" AND name='%q'", name);
     536    }
     537    if (version != NULL) {
     538        version_clause = sqlite3_mprintf(" AND version='%q'", version);
     539    }
     540    if (revision != NULL) {
     541        revision_clause = sqlite3_mprintf(" AND revision='%q'", revision);
     542    }
     543    if (variants != NULL) {
     544        variants_clause = sqlite3_mprintf(" AND variants='%q'", variants);
     545    }
     546    query = sqlite3_mprintf("SELECT id FROM ports WHERE (state='imaged' OR "
     547            "state='installed')%s%s%s%s", name_clause,
     548            version_clause, revision_clause, variants_clause);
    486549    result = reg_all_entries(reg, query, -1, entries, errPtr);
    487550    sqlite3_free(query);
     551    if (name_clause != empty) sqlite3_free(name_clause);
     552    if (version_clause != empty) sqlite3_free(version_clause);
     553    if (revision_clause != empty) sqlite3_free(revision_clause);
     554    if (variants_clause != empty) sqlite3_free(variants_clause);
    488555    return result;
    489556}
     
    505572    char* query;
    506573    int result;
    507     char* select = "SELECT registry.ports.id FROM registry.ports";
     574    char* select = "SELECT id FROM registry.ports";
    508575    if (name == NULL) {
    509576        format = "%s WHERE state='installed'";
     
    531598    int result = 0;
    532599    sqlite3_stmt* stmt;
    533     char* query = "SELECT registry.files.id FROM registry.files INNER JOIN "
    534         "registry.ports USING(id) WHERE  registry.ports.state = 'installed' "
    535         "AND registry.files.path=?";
     600    char* query = "SELECT id FROM registry.files WHERE actual_path=? AND active";
    536601    if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
    537602            && (sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC)
     
    578643    sqlite3_stmt* stmt;
    579644    sqlite_int64 result = 0;
    580     char* query = "SELECT registry.files.id FROM registry.files INNER JOIN "
    581         "registry.ports USING(id) WHERE  registry.ports.state = 'installed' "
    582         "AND registry.files.path=?";
     645    char* query = "SELECT id FROM registry.files WHERE actual_path=? AND active";
    583646    if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
    584647            && (sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC)
     
    707770        reg_error* errPtr) {
    708771    reg_registry* reg = entry->reg;
     772    int result = 1;
    709773    sqlite3_stmt* stmt;
    710     char* insert = "INSERT INTO registry.files (id, path) VALUES (?, ?)";
     774    char* insert = "INSERT INTO registry.files (id, path, mtime, active) "
     775        "VALUES (?, ?, 0, 0)";
    711776    if ((sqlite3_prepare(reg->db, insert, -1, &stmt, NULL) == SQLITE_OK)
    712777            && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
    713778        int i;
    714         for (i=0; i<file_count; i++) {
     779        for (i=0; i<file_count && result; i++) {
    715780            if (sqlite3_bind_text(stmt, 2, files[i], -1, SQLITE_STATIC)
    716781                    == SQLITE_OK) {
     
    726791                        default:
    727792                            reg_sqlite_error(reg->db, errPtr, insert);
    728                             sqlite3_finalize(stmt);
    729                             return i;
     793                            result = 0;
     794                            break;
    730795                    }
    731796                } while (r == SQLITE_BUSY);
    732797            } else {
    733798                reg_sqlite_error(reg->db, errPtr, insert);
    734                 sqlite3_finalize(stmt);
    735                 return i;
     799                result = 0;
    736800            }
    737801        }
    738         sqlite3_finalize(stmt);
    739         return file_count;
    740802    } else {
    741803        reg_sqlite_error(reg->db, errPtr, insert);
    742         sqlite3_finalize(stmt);
    743         return 0;
    744     }
     804        result = 0;
     805    }
     806    sqlite3_finalize(stmt);
     807    return result;
    745808}
    746809
     
    758821        reg_error* errPtr) {
    759822    reg_registry* reg = entry->reg;
     823    int result = 1;
    760824    sqlite3_stmt* stmt;
    761     char* query = "DELETE FROM registry.files WHERE id=? AND path=?";
     825    char* query = "DELETE FROM registry.files WHERE path=? AND id=?";
    762826    if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
    763             && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
     827            && (sqlite3_bind_int64(stmt, 2, entry->id) == SQLITE_OK)) {
    764828        int i;
    765         for (i=0; i<file_count; i++) {
    766             if (sqlite3_bind_text(stmt, 2, files[i], -1, SQLITE_STATIC)
     829        for (i=0; i<file_count && result; i++) {
     830            if (sqlite3_bind_text(stmt, 1, files[i], -1, SQLITE_STATIC)
    767831                    == SQLITE_OK) {
    768832                int r;
     
    772836                        case SQLITE_DONE:
    773837                            if (sqlite3_changes(reg->db) == 0) {
    774                                 errPtr->code = REG_INVALID;
    775                                 errPtr->description = "this entry does not own "
    776                                     "the given file";
    777                                 errPtr->free = NULL;
    778                                 sqlite3_finalize(stmt);
    779                                 return i;
     838                                reg_throw(errPtr, REG_INVALID, "this entry "
     839                                        "does not own the given file");
     840                                result = 0;
     841                            } else {
     842                                sqlite3_reset(stmt);
    780843                            }
    781                             sqlite3_reset(stmt);
    782844                            break;
    783845                        case SQLITE_BUSY:
     
    785847                        default:
    786848                            reg_sqlite_error(reg->db, errPtr, query);
    787                             sqlite3_finalize(stmt);
    788                             return i;
     849                            result = 0;
     850                            break;
    789851                    }
    790852                } while (r == SQLITE_BUSY);
    791853            } else {
    792854                reg_sqlite_error(reg->db, errPtr, query);
    793                 sqlite3_finalize(stmt);
    794                 return i;
     855                result = 0;
    795856            }
    796857        }
    797         sqlite3_finalize(stmt);
    798         return file_count;
    799858    } else {
    800859        reg_sqlite_error(reg->db, errPtr, query);
    801         sqlite3_finalize(stmt);
    802         return 0;
    803     }
    804 }
    805 
    806 /**
    807  * Gets a list of files owned by the given port.
     860        result = 0;
     861    }
     862    sqlite3_finalize(stmt);
     863    return result;
     864}
     865
     866/**
     867 * Gets a list of files provided by the given port. These files are in the port
     868 * image and do not necessarily correspond to active files on the filesystem.
     869 *
     870 * TODO: check that the port's installtype is image
    808871 *
    809872 * @param [in] entry   entry to get the list for
    810  * @param [out] files  a list of files owned by the port
     873 * @param [out] files  a list of files provided by the port
    811874 * @param [out] errPtr on error, a description of the error that occurred
    812875 * @return             the number of files if success; negative if failure
    813876 */
    814 int reg_entry_files(reg_entry* entry, char*** files, reg_error* errPtr) {
     877int reg_entry_imagefiles(reg_entry* entry, char*** files, reg_error* errPtr) {
    815878    reg_registry* reg = entry->reg;
    816879    sqlite3_stmt* stmt;
     
    859922
    860923/**
     924 * Gets a list of files owned by the given port. These files are active in the
     925 * filesystem and could be different from the port's imagefiles.
     926 *
     927 * TODO: check that the port is active
     928 *
     929 * @param [in] entry   entry to get the list for
     930 * @param [out] files  a list of files owned by the port
     931 * @param [out] errPtr on error, a description of the error that occurred
     932 * @return             the number of files if success; negative if failure
     933 */
     934int reg_entry_files(reg_entry* entry, char*** files, reg_error* errPtr) {
     935    reg_registry* reg = entry->reg;
     936    sqlite3_stmt* stmt;
     937    char* query = "SELECT actual_path FROM registry.files WHERE id=? "
     938        "AND active ORDER BY actual_path";
     939    if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
     940            && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
     941        char** result = malloc(10*sizeof(char*));
     942        int result_count = 0;
     943        int result_space = 10;
     944        int r;
     945        do {
     946            char* element;
     947            r = sqlite3_step(stmt);
     948            switch (r) {
     949                case SQLITE_ROW:
     950                    element = strdup((const char*)sqlite3_column_text(stmt, 0));
     951                    reg_listcat((void***)&result, &result_count, &result_space,
     952                            element);
     953                    break;
     954                case SQLITE_DONE:
     955                case SQLITE_BUSY:
     956                    break;
     957                default:
     958                    reg_sqlite_error(reg->db, errPtr, query);
     959                    break;
     960            }
     961        } while (r == SQLITE_ROW || r == SQLITE_BUSY);
     962        sqlite3_finalize(stmt);
     963        if (r == SQLITE_DONE) {
     964            *files = result;
     965            return result_count;
     966        } else {
     967            int i;
     968            for (i=0; i<result_count; i++) {
     969                free(result[i]);
     970            }
     971            free(result);
     972            return -1;
     973        }
     974    } else {
     975        reg_sqlite_error(reg->db, errPtr, query);
     976        sqlite3_finalize(stmt);
     977        return -1;
     978    }
     979}
     980
     981/**
     982 * Sets an entry's files as being active in the filesystem. This entry will be
     983 * subsequently returned by `reg_entry_owner` on those files' path. If all files
     984 * are being activated as the names they are in the registry, then `as_files`
     985 * may be NULL. If they are being activated to different paths than the original
     986 * files, then `as_files` should be a list of the same length as `files`.
     987 *
     988 * @param [in] entry      entry to assign the file to
     989 * @param [in] files      a list of files to activate
     990 * @param [in] as_files   NULL, or a list of paths the files are activated as
     991 * @param [in] file_count number of files to activate
     992 * @param [out] errPtr    on error, a description of the error that occurred
     993 * @return                true if success; false if failure
     994 */
     995int reg_entry_activate(reg_entry* entry, char** files, char** as_files,
     996        int file_count, reg_error* errPtr) {
     997    reg_registry* reg = entry->reg;
     998    int result = 1;
     999    int i;
     1000    sqlite3_stmt* select;
     1001    sqlite3_stmt* update;
     1002    char* select_query = "SELECT id FROM registry.files WHERE actual_path=? "
     1003        "AND active";
     1004    char* update_query = "UPDATE registry.files SET actual_path=?, active=1 "
     1005        "WHERE path=? AND id=?";
     1006
     1007    /* if as_files wasn't specified, activate as the original files */
     1008    if (as_files == NULL) {
     1009        as_files = files;
     1010    }
     1011
     1012    if (sqlite3_prepare(reg->db, select_query, -1, &select, NULL) == SQLITE_OK){
     1013        if ((sqlite3_prepare(reg->db, update_query, -1, &update, NULL)
     1014                == SQLITE_OK)
     1015                && (sqlite3_bind_int64(update, 3, entry->id) == SQLITE_OK)) {
     1016            for (i=0; i<file_count && result; i++) {
     1017                if ((sqlite3_bind_text(select, 1, files[i], -1, SQLITE_STATIC)
     1018                            == SQLITE_OK)
     1019                        && (sqlite3_bind_text(update, 1, as_files[i], -1,
     1020                                SQLITE_STATIC) == SQLITE_OK)
     1021                        && (sqlite3_bind_text(update, 2, files[i], -1,
     1022                                SQLITE_STATIC) == SQLITE_OK)) {
     1023                    int r;
     1024                    do {
     1025                        r = sqlite3_step(select);
     1026                        switch (r) {
     1027                            case SQLITE_ROW:
     1028                                reg_throw(errPtr, REG_ALREADY_ACTIVE, "%s is "
     1029                                        "being used by another port", files[i]);
     1030                                result = 0;
     1031                                break;
     1032                            case SQLITE_DONE:
     1033                                do {
     1034                                    r = sqlite3_step(update);
     1035                                    switch (r) {
     1036                                        case SQLITE_DONE:
     1037                                            if (sqlite3_changes(reg->db) == 0) {
     1038                                                reg_throw(errPtr, REG_INVALID,
     1039                                                        "%s is not provided by "
     1040                                                        "this port", files[i]);
     1041                                                result = 0;
     1042                                            } else {
     1043                                                sqlite3_reset(select);
     1044                                                sqlite3_reset(update);
     1045                                            }
     1046                                            break;
     1047                                        case SQLITE_BUSY:
     1048                                            break;
     1049                                        case SQLITE_ERROR:
     1050                                            reg_sqlite_error(reg->db, errPtr,
     1051                                                    update_query);
     1052                                            result = 0;
     1053                                            break;
     1054                                    }
     1055                                } while (r == SQLITE_BUSY);
     1056                                break;
     1057                            case SQLITE_BUSY:
     1058                                break;
     1059                            case SQLITE_ERROR:
     1060                                reg_sqlite_error(reg->db, errPtr, select_query);
     1061                                result = 0;
     1062                                break;
     1063                        }
     1064                    } while (r == SQLITE_BUSY);
     1065                } else {
     1066                    reg_sqlite_error(reg->db, errPtr, NULL);
     1067                    result = 0;
     1068                }
     1069            }
     1070        } else {
     1071            reg_sqlite_error(reg->db, errPtr, update_query);
     1072            result = 0;
     1073        }
     1074        sqlite3_finalize(update);
     1075    } else {
     1076        reg_sqlite_error(reg->db, errPtr, select_query);
     1077        result = 0;
     1078    }
     1079    sqlite3_finalize(select);
     1080    return result;
     1081}
     1082
     1083/**
     1084 * Deactivates files owned by a given entry. That entry's version of all files
     1085 * must currently be active.
     1086 *
     1087 * @param [in] entry      current owner of the files
     1088 * @param [in] files      a list of files to deactivate
     1089 * @param [in] file_count number of files to deactivate
     1090 * @param [out] errPtr    on error, a description of the error that occurred
     1091 * @return                true if success; false if failure
     1092 */
     1093int reg_entry_deactivate(reg_entry* entry, char** files, int file_count,
     1094        reg_error* errPtr) {
     1095    reg_registry* reg = entry->reg;
     1096    int result = 1;
     1097    int i;
     1098    sqlite3_stmt* stmt;
     1099    char* query = "UPDATE registry.files SET active=0 WHERE actual_path=? AND id=?";
     1100    if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
     1101            && (sqlite3_bind_int64(stmt, 2, entry->id) == SQLITE_OK)) {
     1102        for (i=0; i<file_count && result; i++) {
     1103            if (sqlite3_bind_text(stmt, 1, files[i], -1, SQLITE_STATIC)
     1104                    == SQLITE_OK) {
     1105                int r;
     1106                do {
     1107                    r = sqlite3_step(stmt);
     1108                    switch (r) {
     1109                        case SQLITE_DONE:
     1110                            if (sqlite3_changes(reg->db) == 0) {
     1111                                reg_throw(errPtr, REG_INVALID, "this entry "
     1112                                        "does not own the given file");
     1113                                result = 0;
     1114                            } else {
     1115                                sqlite3_reset(stmt);
     1116                            }
     1117                            break;
     1118                        case SQLITE_BUSY:
     1119                            break;
     1120                        default:
     1121                            reg_sqlite_error(reg->db, errPtr, query);
     1122                            result = 0;
     1123                            break;
     1124                    }
     1125                } while (r == SQLITE_BUSY);
     1126            } else {
     1127                reg_sqlite_error(reg->db, errPtr, query);
     1128                result = 0;
     1129            }
     1130        }
     1131    } else {
     1132        reg_sqlite_error(reg->db, errPtr, query);
     1133        result = 0;
     1134    }
     1135    sqlite3_finalize(stmt);
     1136    return result;
     1137}
     1138
     1139/**
     1140 * Gets a list of ports that depend on this one. Uninstalling the given port
     1141 * could potentially break any port listed in its dependents, and could not
     1142 * break any other.
     1143 *
     1144 * N.B.: an inactive port has no dependents, since it can be safely removed.
     1145 *
     1146 * @param [in] entry       a port
     1147 * @param [out] dependents a list of ports dependent on the given port
     1148 * @param [out] errPtr     on error, a description of the error that occurred
     1149 * @return                 true if success; false if failure
     1150 */
     1151int reg_entry_dependents(reg_entry* entry, reg_entry*** dependents,
     1152        reg_error* errPtr) {
     1153    reg_registry* reg = entry->reg;
     1154    char* query = sqlite3_mprintf("SELECT dependent.id FROM ports port "
     1155            "INNER JOIN dependencies USING(name) INNER JOIN ports dependent "
     1156            "USING(id) WHERE port.id=%lld AND port.state = 'installed' "
     1157            "AND dependent.state = 'installed'",
     1158            entry->id);
     1159    int result = reg_all_entries(reg, query, -1, dependents, errPtr);
     1160    sqlite3_free(query);
     1161    return result;
     1162}
     1163
     1164/**
     1165 * Gets a list of ports that this one depends on. This only returns ports which
     1166 * have state "installed" and should really only be called upon ports which have
     1167 * state "installed" themselves. Uninstalling any port in this list could break
     1168 * the given port, but uninstalling any other could not break it.
     1169 *
     1170 * @param [in] entry         a port
     1171 * @param [out] dependencies a list of ports the given port depends on
     1172 * @param [out] errPtr       on error, a description of the error that occurred
     1173 * @return                   number of deps if success; negative if failure
     1174 */
     1175int reg_entry_dependencies(reg_entry* entry, reg_entry*** dependencies,
     1176        reg_error* errPtr) {
     1177    reg_registry* reg = entry->reg;
     1178    char* query = sqlite3_mprintf("SELECT ports.id FROM registry.dependencies "
     1179        "INNER JOIN registry.ports USING(name) WHERE dependencies.id=%lld AND "
     1180        "ports.state = 'installed'", entry->id);
     1181    int result = reg_all_entries(reg, query, -1, dependencies, errPtr);
     1182    sqlite3_free(query);
     1183    return result;
     1184}
     1185
     1186/**
     1187 * Sets the given port to depend on the named port. This is a weak link; it
     1188 * refers to a name and not an actual port.
     1189 *
     1190 * @param [in] entry   a port
     1191 * @param [in] name    the name of a port the given port depends on
     1192 * @param [out] errPtr on error, a description of the error that occurred
     1193 * @return             true if success; false if failure
     1194 */
     1195int reg_entry_depends(reg_entry* entry, char* name, reg_error* errPtr) {
     1196    reg_registry* reg = entry->reg;
     1197    int result = 0;
     1198    sqlite3_stmt* stmt;
     1199    char* query = "INSERT INTO registry.dependencies (id, name) VALUES (?,?)";
     1200    if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
     1201            && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)
     1202            && (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC)
     1203                == SQLITE_OK)) {
     1204        int r;
     1205        do {
     1206            r = sqlite3_step(stmt);
     1207            switch (r) {
     1208                case SQLITE_DONE:
     1209                    result = 1;
     1210                    break;
     1211                case SQLITE_BUSY:
     1212                    break;
     1213                default:
     1214                    reg_sqlite_error(reg->db, errPtr, query);
     1215                    break;
     1216            }
     1217        } while (r == SQLITE_BUSY);
     1218    } else {
     1219        reg_sqlite_error(reg->db, errPtr, query);
     1220    }
     1221    sqlite3_finalize(stmt);
     1222    return result;
     1223}
     1224
     1225/**
    8611226 * Fetches a list of all open entries.
    8621227 *
  • trunk/base/src/cregistry/entry.h

    r27967 r28029  
    6161        int strategy, reg_entry*** entries, reg_error* errPtr);
    6262
    63 int reg_entry_imaged(reg_registry* reg, char* name, char* version,
    64         reg_entry*** entries, reg_error* errPtr);
     63int reg_entry_imaged(reg_registry* reg, const char* name, const char* version,
     64        const char* revision, const char* variants, reg_entry*** entries,
     65        reg_error* errPtr);
    6566int reg_entry_installed(reg_registry* reg, char* name, reg_entry*** entries,
    6667        reg_error* errPtr);
     
    8081
    8182int reg_entry_files(reg_entry* entry, char*** files, reg_error* errPtr);
     83int reg_entry_imagefiles(reg_entry* entry, char*** files, reg_error* errPtr);
     84
     85int reg_entry_activate(reg_entry* entry, char** files, char** as_files,
     86        int file_count, reg_error* errPtr);
     87int reg_entry_deactivate(reg_entry* entry, char** files, int file_count,
     88        reg_error* errPtr);
     89
     90int reg_entry_dependents(reg_entry* entry, reg_entry*** dependents,
     91        reg_error* errPtr);
     92int reg_entry_dependencies(reg_entry* entry, reg_entry*** dependencies,
     93        reg_error* errPtr);
     94int reg_entry_depends(reg_entry* entry, char* name, reg_error* errPtr);
    8295
    8396int reg_all_open_entries(reg_registry* reg, reg_entry*** entries);
  • trunk/base/src/cregistry/registry.c

    r27967 r28029  
    3535#include <stdlib.h>
    3636#include <libgen.h>
     37#include <stdarg.h>
    3738#include <sqlite3.h>
    3839#include <sys/stat.h>
     
    4344
    4445/*
    45  * TODO: create a better system for throwing errors. It'd be really awesome to
    46  *       have e.g. `reg_throw_error(code, fmt, ...)` but I don't feel like
    47  *       messing with varargs right now.
    48  *
    4946 * TODO: maybe all the errPtrs could be made a property of `reg_registry`
    5047 *       instead, and be destroyed with it? That would make memory-management
     
    8582                "executing query: %s", sqlite3_errmsg(db), query);
    8683    }
     84}
     85
     86void reg_throw(reg_error* errPtr, char* code, char* fmt, ...) {
     87    va_list list;
     88    va_start(list, fmt);
     89    errPtr->description = sqlite3_vmprintf(fmt, list);
     90    va_end(list);
     91
     92    errPtr->code = code;
     93    errPtr->free = (reg_error_destructor*)sqlite3_free;
    8794}
    8895
     
    126133        return 1;
    127134    } else {
    128         errPtr->code = REG_SQLITE_ERROR;
    129         errPtr->description = sqlite3_mprintf("registry db not closed "
    130                 "correctly (%s)\n", sqlite3_errmsg(reg->db));
    131         errPtr->free = (reg_error_destructor*)sqlite3_free;
     135        reg_throw(errPtr, REG_SQLITE_ERROR, "registry db not closed correctly "
     136                "(%s)\n", sqlite3_errmsg(reg->db));
    132137        return 0;
    133138    }
     
    150155    int result = 0;
    151156    if (reg->status & reg_attached) {
    152         errPtr->code = REG_MISUSE;
    153         errPtr->description = "a database is already attached to this registry";
    154         errPtr->free = NULL;
     157        reg_throw(errPtr, REG_MISUSE, "a database is already attached to this "
     158                "registry");
    155159        return 0;
    156160    }
     
    209213        sqlite3_free(query);
    210214    } else {
    211         errPtr->code = REG_CANNOT_INIT;
    212         errPtr->description = sqlite3_mprintf("port registry doesn't exist at "
     215        reg_throw(errPtr, REG_CANNOT_INIT, "port registry doesn't exist at "
    213216                "\"%q\" and couldn't write to this location", path);
    214         errPtr->free = (reg_error_destructor*)sqlite3_free;
    215217    }
    216218    return result;
     
    231233    char* query = "DETACH DATABASE registry";
    232234    if (!(reg->status & reg_attached)) {
    233         errPtr->code = REG_MISUSE;
    234         errPtr->description = "no database is attached to this registry";
    235         errPtr->free = NULL;
     235        reg_throw(errPtr,REG_MISUSE,"no database is attached to this registry");
    236236        return 0;
    237237    }
     
    276276static int reg_start(reg_registry* reg, const char* query, reg_error* errPtr) {
    277277    if (reg->status & reg_transacting) {
    278         errPtr->code = REG_MISUSE;
    279         errPtr->description = "couldn't start transaction because a "
    280             "transaction is already open";
     278        reg_throw(errPtr, REG_MISUSE, "couldn't start transaction because a "
     279                "transaction is already open");
    281280        errPtr->free = NULL;
    282281        return 0;
     
    334333static int reg_end(reg_registry* reg, const char* query, reg_error* errPtr) {
    335334    if (!(reg->status & reg_transacting)) {
    336         errPtr->code = REG_MISUSE;
    337         errPtr->description = "couldn't end transaction because no transaction "
    338             "is open";
    339         errPtr->free = NULL;
     335        reg_throw(errPtr, REG_MISUSE, "couldn't end transaction because no "
     336                "transaction is open");
    340337        return 0;
    341338    } else {
  • trunk/base/src/cregistry/registry.h

    r27967 r28029  
    4242#define REG_MISUSE          "registry::misuse"
    4343#define REG_CANNOT_INIT     "registry::cannot-init"
     44#define REG_ALREADY_ACTIVE  "registry::already-active"
    4445
    4546typedef void reg_error_destructor(const char* description);
     
    5354void reg_sqlite_error(sqlite3* db, reg_error* errPtr, char* query);
    5455void reg_error_destruct(reg_error* errPtr);
     56void reg_throw(reg_error* errPtr, char* code, char* fmt, ...);
    5557
    5658typedef int (cast_function)(void* userdata, void** dst, void* src,
  • trunk/base/src/cregistry/sql.c

    r27967 r28029  
    296296
    297297        /* file map */
    298         "CREATE TABLE registry.files (id, path, mtime)",
     298        "CREATE TABLE registry.files (id, path, actual_path, active, mtime, "
     299            "md5sum, editable)",
    299300        "CREATE INDEX registry.file_port ON files (id)",
     301        "CREATE INDEX registry.file_path ON files(path)",
     302        "CREATE INDEX registry.file_actual ON files(actual_path)",
     303
     304        /* dependency map */
     305        "CREATE TABLE registry.dependencies (id, name)",
     306        "CREATE INDEX registry.dep_name ON dependencies (name)",
    300307
    301308        "COMMIT",
  • trunk/base/src/registry2.0/Makefile

    r27967 r28029  
    11# $Id$
     2SRCS=           registry_util.tcl portimage.tcl portuninstall.tcl
    23OBJS=       registry.o util.o \
    34                        entry.o entryobj.o
     
    1617test:: ${SHLIB_NAME}
    1718        ${TCLSH} tests/entry.tcl ${SHLIB_NAME}
     19        ${TCLSH} tests/depends.tcl ${SHLIB_NAME}
     20
     21install::
     22        $(INSTALL) -d -o ${DSTUSR} -g ${DSTGRP} -m ${DSTMODE} ${INSTALLDIR}
     23        $(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 ${SHLIB_NAME} ${INSTALLDIR}
     24        $(SILENT) set -x; for file in ${SRCS}; do \
     25                $(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 $$file ${INSTALLDIR}/$$file; \
     26        done
     27        $(SILENT) $(TCLSH) ../pkg_mkindex.tcl ${INSTALLDIR}
  • trunk/base/src/registry2.0/entry.c

    r27967 r28029  
    6767}
    6868
    69 /**
    70  * Sets a given name to be an entry object.
    71  *
    72  * @param [in] interp  Tcl interpreter to create the entry within
    73  * @param [in] name    name to associate the given entry with
    74  * @param [in] entry   entry to associate with the given name
    75  * @param [out] errPtr description of error if it couldn't be set
    76  * @return             true if success; false if failure
    77  * @see set_object
    78  */
    79 static int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
    80         reg_error* errPtr) {
    81     if (set_object(interp, name, entry, "entry", entry_obj_cmd, NULL,
    82                 errPtr)) {
    83         int size = strlen(name) + 1;
    84         entry->proc = malloc(size*sizeof(char));
    85         memcpy(entry->proc, name, size);
    86         return 1;
    87     }
    88     return 0;
    89 }
    90 
    9169static int obj_to_entry(Tcl_Interp* interp, reg_entry** entry, Tcl_Obj* obj,
    9270        reg_error* errPtr) {
     
    10078}
    10179
     80/*
    10281static int list_obj_to_entry(Tcl_Interp* interp, reg_entry*** entries,
    10382        const Tcl_Obj** objv, int objc, reg_error* errPtr) {
     
    10584            (void**)objv, objc, errPtr);
    10685}
    107 
    108 static int entry_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_entry* entry,
    109         reg_error* errPtr) {
    110     if (entry->proc == NULL) {
    111         char* name = unique_name(interp, "registry::entry");
    112         if (!set_entry(interp, name, entry, errPtr)) {
    113             free(name);
    114             return 0;
    115         }
    116         free(name);
    117     }
    118     *obj = Tcl_NewStringObj(entry->proc, -1);
    119     return 1;
    120 }
    121 
    122 static int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
    123         reg_entry** entries, int entry_count, reg_error* errPtr) {
    124     return recast(interp, (cast_function*)entry_to_obj, NULL, (void***)objs,
    125             (void**)entries, entry_count, errPtr);
    126 }
    127 
    128 
    129 /**
     86*/
     87
     88
     89/*
    13090 * registry::entry create portname version revision variants epoch
    13191 *
     
    162122}
    163123
    164 /**
     124/*
    165125 * registry::entry delete entry
    166126 *
     
    202162}
    203163
    204 /**
     164/*
    205165 * registry::entry open portname version revision variants epoch ?name?
    206166 *
     
    348308}
    349309
    350 /**
     310/*
    351311 * registry::entry exists name
    352312 *
     
    370330}
    371331
    372 /**
     332/*
    373333 * registry::entry imaged ?name? ?version?
    374334 *
     
    383343 * installed`). That is, these ports are available but cannot meet dependencies.
    384344 *
     345 * I would have liked to allow implicit variants, but there's no convenient way
     346 * to distinguish between variants not being specified and being specified as
     347 * empty. So, if a revision is specified, so must variants be.
     348 *
    385349 * TODO: add more arguments (epoch, revision, variants), maybe
    386350 */
    387351static int entry_imaged(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
    388352    reg_registry* reg = registry_for(interp, reg_attached);
    389     if (objc > 4) {
    390         Tcl_WrongNumArgs(interp, 2, objv, "?name? ?version?");
    391         return TCL_ERROR;
    392     } else if (reg == NULL) {
    393         return TCL_ERROR;
    394     } else {
    395         char* name = (objc >= 3) ? Tcl_GetString(objv[2]) : NULL;
    396         char* version = (objc == 4) ? Tcl_GetString(objv[3]) : NULL;
     353    if (objc == 5 || objc > 6) {
     354        Tcl_WrongNumArgs(interp, 2, objv, "?name ?version ?revision variants???");
     355        return TCL_ERROR;
     356    } else if (reg == NULL) {
     357        return TCL_ERROR;
     358    } else {
     359        const char* name = (objc >= 3) ? string_or_null(objv[2]) : NULL;
     360        const char* version = (objc >= 4) ? string_or_null(objv[3]) : NULL;
     361        const char* revision = (objc >= 6) ? string_or_null(objv[4]) : NULL;
     362        const char* variants = (revision != 0) ? Tcl_GetString(objv[5]) : NULL;
    397363        reg_entry** entries;
    398364        reg_error error;
    399365        int entry_count;
    400         /* name or version of "" means not specified */
    401         if (name != NULL && *name == '\0') {
    402             name = NULL;
    403         }
    404         if (version != NULL && *version == '\0') {
    405             version = NULL;
    406         }
    407         entry_count = reg_entry_imaged(reg, name, version, &entries, &error);
     366        entry_count = reg_entry_imaged(reg, name, version, revision, variants,
     367                &entries, &error);
    408368        if (entry_count >= 0) {
    409369            Tcl_Obj* resultObj;
     
    420380}
    421381
    422 /**
     382/*
    423383 * registry::entry installed ?name?
    424384 *
     
    463423
    464424
    465 /**
     425/*
    466426 */
    467427static int entry_owner(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
     
    510470};
    511471
    512 /**
     472/*
    513473 * registry::entry cmd ?arg ...?
    514474 *
  • trunk/base/src/registry2.0/entryobj.c

    r27967 r28029  
    8686    } else {
    8787        /* ${entry} prop name value; set a new value */
    88         reg_registry* reg = registry_for(interp, reg_attached | reg_can_write);
     88        reg_registry* reg = registry_for(interp, reg_attached);
    8989        if (reg == NULL) {
    9090            return TCL_ERROR;
     
    104104}
    105105
    106 /*
    107  * ${entry} map file-list
    108  *
    109  * Maps the listed files to the port represented by ${entry}. This will throw an
    110  * error if a file is mapped to an already-existing file, but not a very
    111  * descriptive one.
    112  *
    113  * TODO: more descriptive error on duplicated file
    114  */
    115 static int entry_obj_map(Tcl_Interp* interp, reg_entry* entry, int objc,
    116         Tcl_Obj* CONST objv[]) {
    117     reg_registry* reg = registry_for(interp, reg_attached);
     106typedef struct {
     107    char* name;
     108    int (*function)(reg_entry* entry, char** files, int file_count,
     109            reg_error* errPtr);
     110} filemap_op;
     111
     112static filemap_op filemap_cmds[] = {
     113    { "map", reg_entry_map },
     114    { "unmap", reg_entry_unmap },
     115    { "deactivate", reg_entry_deactivate },
     116    { NULL, NULL }
     117};
     118
     119static int entry_obj_filemap(Tcl_Interp* interp, reg_entry* entry, int objc,
     120        Tcl_Obj* CONST objv[]) {
     121    reg_registry* reg = registry_for(interp, reg_attached);
     122    int op;
    118123    if (objc != 3) {
    119         Tcl_WrongNumArgs(interp, 1, objv, "map file-list");
    120         return TCL_ERROR;
    121     } else if (reg == NULL) {
     124        Tcl_WrongNumArgs(interp, 2, objv, "file-list");
     125        return TCL_ERROR;
     126    } else if (reg == NULL) {
     127        return TCL_ERROR;
     128    } else if (Tcl_GetIndexFromObjStruct(interp, objv[1], filemap_cmds,
     129                sizeof(filemap_op), "cmd", 0, &op) != TCL_OK) {
    122130        return TCL_ERROR;
    123131    } else {
     
    126134        Tcl_Obj** listv;
    127135        int listc;
    128         int i;
    129136        int result = TCL_ERROR;
    130137        if (Tcl_ListObjGetElements(interp, objv[2], &listc, &listv) != TCL_OK) {
     
    132139        }
    133140        if (list_obj_to_string(&files, listv, listc, &error)) {
    134             if (reg_entry_map(entry, files, listc, &error) == listc) {
     141            if (filemap_cmds[op].function(entry, files, listc, &error)) {
    135142                result = TCL_OK;
    136143            } else {
     
    145152}
    146153
    147 /*
    148  * ${entry} unmap file-list
    149  *
    150  * Unmaps the listed files from the given port. Will throw an error if a file
    151  * that is not mapped to the port is attempted to be unmapped.
    152  */
    153 static int entry_obj_unmap(Tcl_Interp* interp, reg_entry* entry, int objc,
    154         Tcl_Obj* CONST objv[]) {
    155     reg_registry* reg = registry_for(interp, reg_attached);
    156     if (objc != 3) {
    157         Tcl_WrongNumArgs(interp, 1, objv, "map file-list");
    158         return TCL_ERROR;
    159     } else if (reg == NULL) {
    160         return TCL_ERROR;
    161     } else {
    162         char** files;
    163         reg_error error;
    164         Tcl_Obj** listv;
    165         int listc;
    166         int i;
    167         int result = TCL_ERROR;
    168         if (Tcl_ListObjGetElements(interp, objv[2], &listc, &listv) != TCL_OK) {
    169             return TCL_ERROR;
    170         }
    171         if (list_obj_to_string(&files, listv, listc, &error)) {
    172             if (reg_entry_unmap(entry, files, listc, &error) == listc) {
    173                 result = TCL_OK;
    174             } else {
    175                 result = registry_failed(interp, &error);
    176             }
    177             free(files);
    178         } else {
    179             result = registry_failed(interp, &error);
    180         }
    181         return result;
    182     }
    183 }
    184 
    185154static int entry_obj_files(Tcl_Interp* interp, reg_entry* entry, int objc,
    186155        Tcl_Obj* CONST objv[]) {
     
    195164        reg_error error;
    196165        int file_count = reg_entry_files(entry, &files, &error);
     166        int i;
    197167        if (file_count >= 0) {
    198             int i;
    199168            Tcl_Obj** objs;
    200169            int retval = TCL_ERROR;
     
    217186}
    218187
     188static int entry_obj_imagefiles(Tcl_Interp* interp, reg_entry* entry, int objc,
     189        Tcl_Obj* CONST objv[]) {
     190    reg_registry* reg = registry_for(interp, reg_attached);
     191    if (objc != 2) {
     192        Tcl_WrongNumArgs(interp, 1, objv, "files");
     193        return TCL_ERROR;
     194    } else if (reg == NULL) {
     195        return TCL_ERROR;
     196    } else {
     197        char** files;
     198        reg_error error;
     199        int file_count = reg_entry_imagefiles(entry, &files, &error);
     200        int i;
     201        if (file_count >= 0) {
     202            Tcl_Obj** objs;
     203            int retval = TCL_ERROR;
     204            if (list_string_to_obj(&objs, files, file_count, &error)) {
     205                Tcl_Obj* result = Tcl_NewListObj(file_count, objs);
     206                Tcl_SetObjResult(interp, result);
     207                free(objs);
     208                retval = TCL_OK;
     209            } else {
     210                retval = registry_failed(interp, &error);
     211            }
     212            for (i=0; i<file_count; i++) {
     213                free(files[i]);
     214            }
     215            free(files);
     216            return retval;
     217        }
     218        return registry_failed(interp, &error);
     219    }
     220}
     221
     222static int entry_obj_activate(Tcl_Interp* interp, reg_entry* entry, int objc,
     223        Tcl_Obj* CONST objv[]) {
     224    reg_registry* reg = registry_for(interp, reg_attached);
     225    if (objc > 4) {
     226        Tcl_WrongNumArgs(interp, 1, objv, "activate file-list ?as-file-list?");
     227        return TCL_ERROR;
     228    } else if (reg == NULL) {
     229        return TCL_ERROR;
     230    } else {
     231        char** files;
     232        char** as_files = NULL;
     233        reg_error error;
     234        Tcl_Obj* as;
     235        Tcl_Obj** as_listv;
     236        Tcl_Obj** listv;
     237        int listc;
     238        int as_listc;
     239        int result = TCL_ERROR;
     240        if (objc >= 4) {
     241            as = objv[3];
     242        } else {
     243            as = NULL;
     244            as_listv = NULL;
     245        }
     246        if (Tcl_ListObjGetElements(interp, objv[2], &listc, &listv) != TCL_OK) {
     247            return TCL_ERROR;
     248        }
     249        if (as != NULL) {
     250            if (Tcl_ListObjGetElements(interp, as, &as_listc, &as_listv)
     251                    != TCL_OK) {
     252                return TCL_ERROR;
     253            }
     254            if (listc != as_listc) {
     255                /* TODO: set an error code */
     256                Tcl_SetResult(interp, "list and as_list must be of equal "
     257                        "length", TCL_STATIC);
     258                return TCL_ERROR;
     259            }
     260        }
     261        if (list_obj_to_string(&files, listv, listc, &error)
     262                && (as_listv == NULL || list_obj_to_string(&as_files, as_listv,
     263                        as_listc, &error))) {
     264            if (reg_entry_activate(entry, files, as_files, listc, &error)) {
     265                result = TCL_OK;
     266            } else {
     267                result = registry_failed(interp, &error);
     268            }
     269            free(files);
     270        } else {
     271            result = registry_failed(interp, &error);
     272        }
     273        return result;
     274    }
     275}
     276
     277static int entry_obj_dependencies(Tcl_Interp* interp, reg_entry* entry,
     278        int objc, Tcl_Obj* CONST objv[]) {
     279    reg_registry* reg = registry_for(interp, reg_attached);
     280    if (objc != 2) {
     281        Tcl_WrongNumArgs(interp, 1, objv, "dependents");
     282        return TCL_ERROR;
     283    } else if (reg == NULL) {
     284        return TCL_ERROR;
     285    } else {
     286        reg_entry** entries;
     287        reg_error error;
     288        int entry_count = reg_entry_dependencies(entry, &entries, &error);
     289        if (entry_count >= 0) {
     290            Tcl_Obj** objs;
     291            int retval = TCL_ERROR;
     292            if (list_entry_to_obj(interp, &objs, entries, entry_count, &error)){
     293                Tcl_Obj* result = Tcl_NewListObj(entry_count, objs);
     294                Tcl_SetObjResult(interp, result);
     295                free(objs);
     296                retval = TCL_OK;
     297            } else {
     298                retval = registry_failed(interp, &error);
     299            }
     300            free(entries);
     301            return retval;
     302        }
     303        return registry_failed(interp, &error);
     304    }
     305}
     306
     307static int entry_obj_dependents(Tcl_Interp* interp, reg_entry* entry, int objc,
     308        Tcl_Obj* CONST objv[]) {
     309    reg_registry* reg = registry_for(interp, reg_attached);
     310    if (objc != 2) {
     311        Tcl_WrongNumArgs(interp, 1, objv, "dependents");
     312        return TCL_ERROR;
     313    } else if (reg == NULL) {
     314        return TCL_ERROR;
     315    } else {
     316        reg_entry** entries;
     317        reg_error error;
     318        int entry_count = reg_entry_dependents(entry, &entries, &error);
     319        if (entry_count >= 0) {
     320            Tcl_Obj** objs;
     321            int retval = TCL_ERROR;
     322            if (list_entry_to_obj(interp, &objs, entries, entry_count, &error)){
     323                Tcl_Obj* result = Tcl_NewListObj(entry_count, objs);
     324                Tcl_SetObjResult(interp, result);
     325                free(objs);
     326                retval = TCL_OK;
     327            } else {
     328                retval = registry_failed(interp, &error);
     329            }
     330            free(entries);
     331            return retval;
     332        }
     333        return registry_failed(interp, &error);
     334    }
     335}
     336
     337static int entry_obj_depends(Tcl_Interp* interp, reg_entry* entry, int objc,
     338        Tcl_Obj* CONST objv[]) {
     339    reg_registry* reg = registry_for(interp, reg_attached);
     340    if (objc != 3) {
     341        Tcl_WrongNumArgs(interp, 1, objv, "depends portname");
     342        return TCL_ERROR;
     343    } else if (reg == NULL) {
     344        return TCL_ERROR;
     345    } else {
     346        char* port = Tcl_GetString(objv[2]);
     347        reg_error error;
     348        if (reg_entry_depends(entry, port, &error)) {
     349            return TCL_OK;
     350        }
     351        return registry_failed(interp, &error);
     352    }
     353}
     354
    219355typedef struct {
    220356    char* name;
     
    224360
    225361static entry_obj_cmd_type entry_cmds[] = {
     362    /* keys */
    226363    { "name", entry_obj_prop },
    227364    { "portfile", entry_obj_prop },
     
    236373    { "state", entry_obj_prop },
    237374    { "installtype", entry_obj_prop },
    238     { "map", entry_obj_map },
    239     { "unmap", entry_obj_unmap },
     375    /* filemap */
     376    { "map", entry_obj_filemap },
     377    { "unmap", entry_obj_filemap },
    240378    { "files", entry_obj_files },
     379    { "imagefiles", entry_obj_imagefiles },
     380    { "activate", entry_obj_activate },
     381    { "deactivate", entry_obj_filemap },
     382    /* dep map */
     383    { "dependents", entry_obj_dependents },
     384    { "dependencies", entry_obj_dependencies },
     385    { "depends", entry_obj_depends },
    241386    { NULL, NULL }
    242387};
  • trunk/base/src/registry2.0/tests/common.tcl

    r27967 r28029  
    3535}
    3636
     37proc test_set {statement value} {
     38    uplevel 1 "\
     39        puts -nonewline {checking if $statement is \[list $value\]... }
     40        if {\[catch {
     41                set actual \[lsort $statement\]
     42                if {\$actual == \[lsort \[subst {\[list $value\]}\]\]} { \n\
     43                    puts yes
     44                } else { \n\
     45                    puts \"no (was \$actual)\" \n\
     46                    exit 1 \n\
     47                } \n\
     48            } msg\]} { \n\
     49                puts \"caught error: \$msg\" \n\
     50                exit 1 \n\
     51            }"
     52}
     53
    3754proc test_throws {statement error} {
    3855    uplevel 1 "\
  • trunk/base/src/registry2.0/tests/entry.tcl

    r27967 r28029  
    11# $Id$
    2 # Test file for registry::item
     2# Test file for registry::entry
    33# Syntax:
    4 # tclsh item.tcl <Pextlib name>
     4# tclsh entry.tcl <Pextlib name>
    55
    66proc main {pextlibname} {
    77    load $pextlibname
    88
    9         file delete [glob -nocomplain test.db*]
     9    # totally lame that file delete won't do it
     10        eval exec rm -f [glob -nocomplain test.db*]
    1011
    1112    # can't create registry in some brain-dead place or in protected place
    1213    test_throws {registry::open /some/brain/dead/place} registry::cannot-init
    13     test_throws {registry::open /etc/macports_test~} registry::cannot-init
     14    test_throws {registry::open /etc/macports_test_prot~} registry::cannot-init
    1415
    1516    # can't use registry before it's opened
     
    2627
    2728        # create some (somewhat contrived) ports to play with
    28         set vim1 [registry::entry create vim 7.1.000 0 {multibyte +} 0]
     29        set vim1 [registry::entry create vim 7.1.000 0 +cscope+multibyte 0]
    2930        set vim2 [registry::entry create vim 7.1.002 0 {} 0]
    30         set vim3 [registry::entry create vim 7.1.002 0 {multibyte +} 0]
     31        set vim3 [registry::entry create vim 7.1.002 0 +cscope+multibyte 0]
    3132        set zlib [registry::entry create zlib 1.2.3 1 {} 0]
    32         set pcre [registry::entry create pcre 7.1 1 {utf8 +} 0]
     33        set pcre [registry::entry create pcre 7.1 1 +utf8 0]
    3334
    3435        # check that their properties can be set
     
    3839        $zlib state installed
    3940        $pcre state imaged
     41
     42        $vim1 installtype image
     43        $vim2 installtype image
     44        $vim3 installtype image
     45        $zlib installtype direct
     46        $pcre installtype image
    4047
    4148    }
     
    4855        test_equal {[$vim3 version]} 7.1.002
    4956        test_equal {[$zlib revision]} 1
    50         test_equal {[$pcre variants]} {utf8 +}
     57        test_equal {[$pcre variants]} +utf8
    5158   
    5259        # check that imaged and installed give correct results
    53         # have to sort these because their orders aren't defined
    54         set imaged [registry::entry imaged]
    55         test_equal {[lsort $imaged]} {[lsort "$vim1 $vim2 $vim3 $zlib $pcre"]}
     60        test_set {[registry::entry imaged]} {$vim1 $vim2 $vim3 $zlib $pcre}
     61        test_set {[registry::entry installed]} {$vim3 $zlib}
     62        test_set {[registry::entry imaged vim]} {$vim1 $vim2 $vim3}
     63        test_set {[registry::entry imaged vim 7.1.000]} {$vim1}
     64        test_set {[registry::entry imaged vim 7.1.002]} {$vim2 $vim3}
     65        test_set {[registry::entry imaged vim 7.1.002 0 {}]} {$vim2}
     66        test_set {[registry::entry imaged vim 7.1.002 0 +cscope+multibyte]} \
     67            {$vim3}
    5668
    57         set installed [registry::entry installed]
    58         test_equal {[lsort $installed]} {[lsort "$vim3 $zlib"]}
     69        # try searching for ports
     70        # note that 7.1.2 != 7.1.002 but the VERSION collation should be smart
     71        # enough to ignore the zeroes
     72        test_set {[registry::entry search name vim version 7.1.2]} {$vim2 $vim3}
     73        test_set {[registry::entry search variants {}]} {$vim2 $zlib}
     74        test_set {[registry::entry search -glob name vi*]} {$vim1 $vim2 $vim3}
     75        test_set {[registry::entry search -regexp name {zlib|pcre}]} \
     76            {$zlib $pcre}
    5977    }
    60 
    61 
    62     # try searching for ports
    63     # note that 7.1.2 != 7.1.002 but the VERSION collation should be smart
    64     # enough to ignore the zeroes
    65     set vim71002 [registry::entry search name vim version 7.1.2]
    66     test_equal {[lsort $vim71002]} {[lsort "$vim2 $vim3"]}
    67 
    68     set no_variants [registry::entry search variants {}]
    69     test_equal {[lsort $no_variants]} {[lsort "$vim2 $zlib"]}
    70 
    71     set vistar [registry::entry search -glob name vi*]
    72     test_equal {[lsort $vistar]} {[lsort "$vim1 $vim2 $vim3"]}
    73 
    74     set zlibpcre [registry::entry search -regexp name {zlib|pcre}]
    75     test_equal {[lsort $zlibpcre]} {[lsort "$zlib $pcre"]}
    7678
    7779    # try mapping files and checking their owners
    7880    registry::write {
    79         $vim3 map [list /opt/local/bin/vim]
    80         $vim3 map [list /opt/local/bin/vimdiff /opt/local/bin/vimtutor]
     81
     82        test_equal {[registry::entry owner /opt/local/bin/vimtutor]} {}
     83        test_equal {[$vim3 files]} {}
     84
     85        $vim1 map {}
     86        $vim1 map /opt/local/bin/vim
     87        $vim1 map [list /opt/local/bin/vimdiff /opt/local/bin/vimtutor]
     88        $vim2 map [$vim1 imagefiles]
     89        $vim3 map [$vim1 imagefiles]
     90        test_equal {[registry::entry owner /opt/local/bin/vimtutor]} {}
     91        test_equal {[$vim3 files]} {}
     92
     93        $vim3 activate [$vim3 imagefiles]
    8194        test_equal {[registry::entry owner /opt/local/bin/vimtutor]} {$vim3}
    8295        test_equal {[registry::entry owner /opt/local/bin/emacs]} {}
    8396
    84         # don't have to sort because order is defined as alpha
    85         test_equal {[$vim3 files]} {[list /opt/local/bin/vim \
    86             /opt/local/bin/vimdiff /opt/local/bin/vimtutor]}
    87         test_equal {[$zlib files]} {[list]}
     97        test_set {[$vim3 imagefiles]} {/opt/local/bin/vim \
     98            /opt/local/bin/vimdiff /opt/local/bin/vimtutor}
     99        test_set {[$vim3 files]} [$vim3 imagefiles]
     100        test_set {[$zlib imagefiles]} {}
     101
     102        # try activating over files
     103        test_throws {$vim2 activate [$vim2 imagefiles]} registry::already-active
    88104
    89105        # try unmapping and remapping
    90         $vim3 unmap {/opt/local/bin/vim}
     106        $vim3 unmap /opt/local/bin/vimtutor
     107        test_equal {[registry::entry owner /opt/local/bin/vimtutor]} {}
     108
     109        $vim3 deactivate /opt/local/bin/vim
    91110        test_equal {[registry::entry owner /opt/local/bin/vim]} {}
    92         $vim3 map {/opt/local/bin/vim}
     111        $vim3 unmap /opt/local/bin/vim
     112        test_equal {[registry::entry owner /opt/local/bin/vim]} {}
     113        $vim3 map /opt/local/bin/vim
     114        test_equal {[registry::entry owner /opt/local/bin/vim]} {}
     115        $vim3 activate /opt/local/bin/vim
     116        puts [$vim3 files]
     117        puts [registry::entry owner /opt/local/bin/vim]
    93118        test_equal {[registry::entry owner /opt/local/bin/vim]} {$vim3}
     119
     120        # activate to a different location
     121        $vim3 deactivate /opt/local/bin/vimdiff
     122        $vim3 activate /opt/local/bin/vimdiff /opt/local/bin/vimdiff.0
     123        $vim2 activate /opt/local/bin/vimdiff
     124        test_set {[$vim3 files]} {/opt/local/bin/vim /opt/local/bin/vimdiff.0}
     125        test_set {[$vim3 imagefiles]} {/opt/local/bin/vim \
     126            /opt/local/bin/vimdiff}
     127        test_equal {[registry::entry owner /opt/local/bin/vimdiff]} {$vim2}
     128        test_equal {[registry::entry owner /opt/local/bin/vimdiff.0]} {$vim3}
    94129
    95130        # make sure you can't unmap a file you don't own
     
    98133    }
    99134
     135    test_set {[$vim3 imagefiles]} {/opt/local/bin/vim /opt/local/bin/vimdiff}
     136    test_set {[$vim3 files]} {/opt/local/bin/vim /opt/local/bin/vimdiff.0}
     137
    100138    # try some deletions
    101     test_equal {[registry::entry installed zlib]} {$zlib}
    102     test_equal {[registry::entry imaged pcre]} {$pcre}
     139    test_set {[registry::entry installed zlib]} {$zlib}
     140    test_set {[registry::entry imaged pcre]} {$pcre}
    103141
    104142    # try rolling a deletion back
     
    111149    # try actually deleting something
    112150    registry::entry delete $pcre
    113     test_throws {registry::entry open pcre 7.1 1 {utf8 +} 0} \
     151    test_throws {registry::entry open pcre 7.1 1 +utf8 0} \
    114152        registry::not-found
    115153    test {![registry::entry exists $pcre]}
     
    136174
    137175    # check that pcre is still gone
    138     test_throws {registry::entry open pcre 7.1 1 {utf8 +} 0} \
     176    test_throws {registry::entry open pcre 7.1 1 +utf8 0} \
    139177        registry::not-found
    140178
    141179    registry::close
    142180
    143         file delete [glob -nocomplain test.db*]
     181    file delete test.db
    144182}
    145183
  • trunk/base/src/registry2.0/util.c

    r27967 r28029  
    3636
    3737#include "util.h"
     38#include "entryobj.h"
    3839
    3940/**
     
    156157    Tcl_CreateObjCommand(interp, name, proc, value, deleteProc);
    157158    return 1;
     159}
     160
     161/**
     162 * Sets a given name to be an entry object.
     163 *
     164 * @param [in] interp  Tcl interpreter to create the entry within
     165 * @param [in] name    name to associate the given entry with
     166 * @param [in] entry   entry to associate with the given name
     167 * @param [out] errPtr description of error if it couldn't be set
     168 * @return             true if success; false if failure
     169 * @see set_object
     170 */
     171int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
     172        reg_error* errPtr) {
     173    if (set_object(interp, name, entry, "entry", entry_obj_cmd, NULL,
     174                errPtr)) {
     175        int size = strlen(name) + 1;
     176        entry->proc = malloc(size*sizeof(char));
     177        memcpy(entry->proc, name, size);
     178        return 1;
     179    }
     180    return 0;
    158181}
    159182
     
    214237}
    215238
     239const char* string_or_null(Tcl_Obj* obj) {
     240    const char* string = Tcl_GetString(obj);
     241    if (string[0] == '\0') {
     242        return NULL;
     243    } else {
     244        return string;
     245    }
     246}
     247
    216248int recast(void* userdata, cast_function* fn, free_function* del, void*** outv,
    217249        void** inv, int inc, reg_error* errPtr) {
     
    233265}
    234266
     267int entry_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_entry* entry,
     268        reg_error* errPtr) {
     269    if (entry->proc == NULL) {
     270        char* name = unique_name(interp, "registry::entry");
     271        if (!set_entry(interp, name, entry, errPtr)) {
     272            free(name);
     273            return 0;
     274        }
     275        free(name);
     276    }
     277    *obj = Tcl_NewStringObj(entry->proc, -1);
     278    return 1;
     279}
     280
     281int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
     282        reg_entry** entries, int entry_count, reg_error* errPtr) {
     283    return recast(interp, (cast_function*)entry_to_obj, NULL, (void***)objs,
     284            (void**)entries, entry_count, errPtr);
     285}
     286
    235287static int obj_to_string(void* userdata UNUSED, char** string, Tcl_Obj* obj,
    236288        reg_error* errPtr UNUSED) {
     
    239291}
    240292
    241 int list_obj_to_string(char*** strings, const Tcl_Obj** objv, int objc,
     293int list_obj_to_string(char*** strings, Tcl_Obj** objv, int objc,
    242294        reg_error* errPtr) {
    243295    return recast(NULL, (cast_function*)obj_to_string, NULL, (void***)strings,
     
    255307}
    256308
    257 int list_string_to_obj(Tcl_Obj*** objv, const char** strings, int objc,
     309int list_string_to_obj(Tcl_Obj*** objv, char** strings, int objc,
    258310        reg_error* errPtr) {
    259311    return recast(NULL, (cast_function*)string_to_obj, (free_function*)free_obj,
  • trunk/base/src/registry2.0/util.h

    r27967 r28029  
    3737
    3838#include <cregistry/registry.h>
     39#include <cregistry/entry.h>
    3940
    4041typedef struct {
     
    5455int set_object(Tcl_Interp* interp, char* name, void* value, char* type,
    5556        Tcl_ObjCmdProc* proc, Tcl_CmdDeleteProc* deleteProc, reg_error* errPtr);
     57int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
     58        reg_error* errPtr);
    5659
    5760void set_sqlite_result(Tcl_Interp* interp, sqlite3* db, const char* query);
     
    6265        set_object_function* setter);
    6366
     67const char* string_or_null(Tcl_Obj* obj);
     68
    6469int recast(void* userdata, cast_function* fn, free_function* del, void*** outv,
    6570        void** inv, int inc, reg_error* errPtr);
    6671
     72int entry_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_entry* entry,
     73        reg_error* errPtr);
     74int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
     75        reg_entry** entries, int entry_count, reg_error* errPtr);
     76
    6777void free_strings(void* userdata UNUSED, char** strings, int count);
    6878
    69 int list_obj_to_string(char*** strings, const Tcl_Obj** objv, int objc,
     79int list_obj_to_string(char*** strings, Tcl_Obj** objv, int objc,
    7080        reg_error* errPtr);
    71 int list_string_to_obj(Tcl_Obj*** objv, const char** strings, int objc,
     81int list_string_to_obj(Tcl_Obj*** objv, char** strings, int objc,
    7282        reg_error* errPtr);
    7383
Note: See TracChangeset for help on using the changeset viewer.