// Document the packages in the given program static void doc_packages(docgen_t* docgen, ast_t* ast) { assert(ast != NULL); assert(ast_id(ast) == TK_PROGRAM); // The Main package appears first, other packages in alphabetical order ast_t* package_1 = NULL; ast_list_t packages = { NULL, NULL, NULL }; // Sort packages for(ast_t* p = ast_child(ast); p != NULL; p = ast_sibling(p)) { assert(ast_id(p) == TK_PACKAGE); if(strcmp(package_name(p), "$0") == 0) { assert(package_1 == NULL); package_1 = p; } else { const char* name = package_qualified_name(p); doc_list_add(&packages, p, name); } } // Process packages assert(package_1 != NULL); doc_package(docgen, package_1); for(ast_list_t* p = packages.next; p != NULL; p = p->next) doc_package(docgen, p->ast); }
// Process the given provided method for the given entity. // The method passed should be reified already and will be freed by this // function. static bool provided_method(ast_t* entity, ast_t* reified_method, ast_t* raw_method, ast_t** last_method) { assert(entity != NULL); assert(reified_method != NULL); assert(last_method != NULL); const char* entity_name = ast_name(ast_child(entity)); if(ast_id(reified_method) == TK_BE || ast_id(reified_method) == TK_NEW) { // Modify return type to the inheritting type ast_t* ret_type = ast_childidx(reified_method, 4); assert(ast_id(ret_type) == TK_NOMINAL); const char* pkg_name = package_name(ast_nearest(entity, TK_PACKAGE)); ast_set_name(ast_childidx(ret_type, 0), pkg_name); ast_set_name(ast_childidx(ret_type, 1), entity_name); } // Ignore docstring ast_t* doc = ast_childidx(reified_method, 7); if(ast_id(doc) == TK_STRING) { ast_set_name(doc, ""); ast_setid(doc, TK_NONE); } // Check for existing method of the same name const char* name = ast_name(ast_childidx(reified_method, 1)); assert(name != NULL); ast_t* existing = ast_get(entity, name, NULL); if(existing != NULL && is_field(existing)) { ast_error(existing, "field '%s' clashes with provided method", name); ast_error(raw_method, "method is defined here"); ast_free_unattached(reified_method); return false; } existing = add_method(entity, existing, reified_method, last_method); if(existing == NULL) { ast_free_unattached(reified_method); return false; } method_t* info = (method_t*)ast_data(existing); assert(info != NULL); if(!record_default_body(reified_method, raw_method, info)) ast_free_unattached(reified_method); return true; }
static ast_result_t sugar_module(ast_t* ast) { ast_t* docstring = ast_child(ast); ast_t* package = ast_parent(ast); assert(ast_id(package) == TK_PACKAGE); if(strcmp(package_name(package), "$0") != 0) { // Every module not in builtin has an implicit use builtin command. // Since builtin is always the first package processed it is $0. BUILD(builtin, ast, NODE(TK_USE, NONE STRING(stringtab("builtin")) NONE)); ast_add(ast, builtin); } if((docstring == NULL) || (ast_id(docstring) != TK_STRING)) return AST_OK; ast_t* package_docstring = ast_childlast(package); if(ast_id(package_docstring) == TK_STRING) { ast_error(docstring, "the package already has a docstring"); ast_error(package_docstring, "the existing docstring is here"); return AST_ERROR; } ast_append(package, docstring); ast_remove(docstring); return AST_OK; }
ast_t* package_id(ast_t* ast) { return ast_from_string(ast, package_name(ast)); }
int main(int argc, char** argv) { const double PI(3.141592653589793); if (argc != 4) { std::cout << "usage:" << std::endl; std::cout << "package_scene path_scene output" << std::endl; return 0; } ros::init(argc, argv, "dart_test"); ros::NodeHandle nh_; std::string package_name( argv[1] ); std::string scene_urdf( argv[2] ); ros::Rate loop_rate(400); ros::Publisher joint_state_pub_; joint_state_pub_ = nh_.advertise<sensor_msgs::JointState>("/joint_states", 10); tf::TransformBroadcaster br; MarkerPublisher markers_pub(nh_); std::string package_path_barrett = ros::package::getPath("barrett_hand_defs"); std::string package_path = ros::package::getPath(package_name); // Load the Skeleton from a file dart::utils::DartLoader loader; loader.addPackageDirectory("barrett_hand_defs", package_path_barrett); loader.addPackageDirectory("barrett_hand_sim_dart", package_path); boost::shared_ptr<GraspSpecification > gspec = GraspSpecification::readFromUrdf(package_path + scene_urdf); dart::dynamics::SkeletonPtr scene( loader.parseSkeleton(package_path + scene_urdf) ); scene->enableSelfCollision(true); dart::dynamics::SkeletonPtr bh( loader.parseSkeleton(package_path_barrett + "/robots/barrett_hand.urdf") ); Eigen::Isometry3d tf; tf = scene->getBodyNode("gripper_mount_link")->getRelativeTransform(); bh->getJoint(0)->setTransformFromParentBodyNode(tf); dart::simulation::World* world = new dart::simulation::World(); world->addSkeleton(scene); world->addSkeleton(bh); Eigen::Vector3d grav(0,0,-1); world->setGravity(grav); GripperController gc; double Kc = 400.0; double KcDivTi = Kc / 1.0; gc.addJoint("right_HandFingerOneKnuckleOneJoint", Kc, KcDivTi, 0.0, 0.001, 50.0, true, false); gc.addJoint("right_HandFingerOneKnuckleTwoJoint", Kc, KcDivTi, 0.0, 0.001, 50.0, false, true); gc.addJoint("right_HandFingerTwoKnuckleTwoJoint", Kc, KcDivTi, 0.0, 0.001, 50.0, false, true); gc.addJoint("right_HandFingerThreeKnuckleTwoJoint", Kc, KcDivTi, 0.0, 0.001, 50.0, false, true); gc.addJointMimic("right_HandFingerTwoKnuckleOneJoint", 2.0*Kc, KcDivTi, 0.0, 0.001, 50.0, true, "right_HandFingerOneKnuckleOneJoint", 1.0, 0.0); gc.addJointMimic("right_HandFingerOneKnuckleThreeJoint", 2.0*Kc, KcDivTi, 0.0, 0.001, 50.0, false, "right_HandFingerOneKnuckleTwoJoint", 0.333333, 0.0); gc.addJointMimic("right_HandFingerTwoKnuckleThreeJoint", 2.0*Kc, KcDivTi, 0.0, 0.001, 50.0, false, "right_HandFingerTwoKnuckleTwoJoint", 0.333333, 0.0); gc.addJointMimic("right_HandFingerThreeKnuckleThreeJoint", 2.0*Kc, KcDivTi, 0.0, 0.001, 50.0, false, "right_HandFingerThreeKnuckleTwoJoint", 0.333333, 0.0); gc.setGoalPosition("right_HandFingerOneKnuckleOneJoint", gspec->getGoalPosition("right_HandFingerOneKnuckleOneJoint")); gc.setGoalPosition("right_HandFingerOneKnuckleTwoJoint", gspec->getGoalPosition("right_HandFingerOneKnuckleTwoJoint")); gc.setGoalPosition("right_HandFingerTwoKnuckleTwoJoint", gspec->getGoalPosition("right_HandFingerTwoKnuckleTwoJoint")); gc.setGoalPosition("right_HandFingerThreeKnuckleTwoJoint", gspec->getGoalPosition("right_HandFingerThreeKnuckleTwoJoint")); std::map<std::string, double> joint_q_map; joint_q_map["right_HandFingerOneKnuckleOneJoint"] = gspec->getInitPosition("right_HandFingerOneKnuckleOneJoint"); joint_q_map["right_HandFingerTwoKnuckleOneJoint"] = gspec->getInitPosition("right_HandFingerOneKnuckleOneJoint"); joint_q_map["right_HandFingerOneKnuckleTwoJoint"] = gspec->getInitPosition("right_HandFingerOneKnuckleTwoJoint"); joint_q_map["right_HandFingerOneKnuckleThreeJoint"] = 0.333333 * gspec->getInitPosition("right_HandFingerOneKnuckleTwoJoint"); joint_q_map["right_HandFingerTwoKnuckleTwoJoint"] = gspec->getInitPosition("right_HandFingerTwoKnuckleTwoJoint"); joint_q_map["right_HandFingerTwoKnuckleThreeJoint"] = 0.333333 * gspec->getInitPosition("right_HandFingerTwoKnuckleTwoJoint"); joint_q_map["right_HandFingerThreeKnuckleTwoJoint"] = gspec->getInitPosition("right_HandFingerThreeKnuckleTwoJoint"); joint_q_map["right_HandFingerThreeKnuckleThreeJoint"] = 0.333333 * gspec->getInitPosition("right_HandFingerThreeKnuckleTwoJoint"); for (std::vector<std::string >::const_iterator it = gc.getJointNames().begin(); it != gc.getJointNames().end(); it++) { dart::dynamics::Joint *j = bh->getJoint((*it)); j->setActuatorType(dart::dynamics::Joint::FORCE); j->setPositionLimited(true); j->setPosition(0, joint_q_map[(*it)]); } int counter = 0; while (ros::ok()) { world->step(false); for (std::map<std::string, double>::iterator it = joint_q_map.begin(); it != joint_q_map.end(); it++) { dart::dynamics::Joint *j = bh->getJoint(it->first); it->second = j->getPosition(0); } gc.controlStep(joint_q_map); // Compute the joint forces needed to compensate for Coriolis forces and // gravity const Eigen::VectorXd& Cg = bh->getCoriolisAndGravityForces(); for (std::map<std::string, double>::iterator it = joint_q_map.begin(); it != joint_q_map.end(); it++) { dart::dynamics::Joint *j = bh->getJoint(it->first); int qidx = j->getIndexInSkeleton(0); double u = gc.getControl(it->first); double dq = j->getVelocity(0); if (!gc.isBackdrivable(it->first)) { j->setPositionLowerLimit(0, std::max(j->getPositionLowerLimit(0), it->second-0.01)); } if (gc.isStopped(it->first)) { j->setPositionLowerLimit(0, std::max(j->getPositionLowerLimit(0), it->second-0.01)); j->setPositionUpperLimit(0, std::min(j->getPositionUpperLimit(0), it->second+0.01)); // std::cout << it->first << " " << "stopped" << std::endl; } j->setForce(0, 0.02*(u-dq) + Cg(qidx)); } for (int bidx = 0; bidx < bh->getNumBodyNodes(); bidx++) { dart::dynamics::BodyNode *b = bh->getBodyNode(bidx); const Eigen::Isometry3d &tf = b->getTransform(); KDL::Frame T_W_L; EigenTfToKDL(tf, T_W_L); // std::cout << b->getName() << std::endl; publishTransform(br, T_W_L, b->getName(), "world"); } int m_id = 0; for (int bidx = 0; bidx < scene->getNumBodyNodes(); bidx++) { dart::dynamics::BodyNode *b = scene->getBodyNode(bidx); const Eigen::Isometry3d &tf = b->getTransform(); KDL::Frame T_W_L; EigenTfToKDL(tf, T_W_L); publishTransform(br, T_W_L, b->getName(), "world"); for (int cidx = 0; cidx < b->getNumCollisionShapes(); cidx++) { dart::dynamics::ConstShapePtr sh = b->getCollisionShape(cidx); if (sh->getShapeType() == dart::dynamics::Shape::MESH) { std::shared_ptr<const dart::dynamics::MeshShape > msh = std::static_pointer_cast<const dart::dynamics::MeshShape >(sh); m_id = markers_pub.addMeshMarker(m_id, KDL::Vector(), 0, 1, 0, 1, 1, 1, 1, msh->getMeshUri(), b->getName()); } } } markers_pub.publish(); ros::spinOnce(); loop_rate.sleep(); counter++; if (counter < 3000) { } else if (counter == 3000) { dart::dynamics::Joint::Properties prop = bh->getJoint(0)->getJointProperties(); dart::dynamics::FreeJoint::Properties prop_free; prop_free.mName = prop_free.mName; prop_free.mT_ParentBodyToJoint = prop.mT_ParentBodyToJoint; prop_free.mT_ChildBodyToJoint = prop.mT_ChildBodyToJoint; prop_free.mIsPositionLimited = false; prop_free.mActuatorType = dart::dynamics::Joint::VELOCITY; bh->getRootBodyNode()->changeParentJointType<dart::dynamics::FreeJoint >(prop_free); } else if (counter < 4000) { bh->getDof("Joint_pos_z")->setVelocity(-0.1); } else { break; } } // // generate models // const std::string ob_name( "graspable" ); scene->getBodyNode(ob_name)->setFrictionCoeff(0.001); // calculate point clouds for all links and for the grasped object std::map<std::string, pcl::PointCloud<pcl::PointNormal>::Ptr > point_clouds_map; std::map<std::string, pcl::PointCloud<pcl::PrincipalCurvatures>::Ptr > point_pc_clouds_map; std::map<std::string, KDL::Frame > frames_map; std::map<std::string, boost::shared_ptr<std::vector<KDL::Frame > > > features_map; std::map<std::string, boost::shared_ptr<pcl::VoxelGrid<pcl::PointNormal> > > grids_map; for (int skidx = 0; skidx < world->getNumSkeletons(); skidx++) { dart::dynamics::SkeletonPtr sk = world->getSkeleton(skidx); for (int bidx = 0; bidx < sk->getNumBodyNodes(); bidx++) { dart::dynamics::BodyNode *b = sk->getBodyNode(bidx); const Eigen::Isometry3d &tf = b->getTransform(); const std::string &body_name = b->getName(); if (body_name.find("right_Hand") != 0 && body_name != ob_name) { continue; } KDL::Frame T_W_L; EigenTfToKDL(tf, T_W_L); std::cout << body_name << " " << b->getNumCollisionShapes() << std::endl; for (int cidx = 0; cidx < b->getNumCollisionShapes(); cidx++) { dart::dynamics::ConstShapePtr sh = b->getCollisionShape(cidx); if (sh->getShapeType() == dart::dynamics::Shape::MESH) { std::shared_ptr<const dart::dynamics::MeshShape > msh = std::static_pointer_cast<const dart::dynamics::MeshShape >(sh); std::cout << "mesh path: " << msh->getMeshPath() << std::endl; std::cout << "mesh uri: " << msh->getMeshUri() << std::endl; const Eigen::Isometry3d &tf = sh->getLocalTransform(); KDL::Frame T_L_S; EigenTfToKDL(tf, T_L_S); KDL::Frame T_S_L = T_L_S.Inverse(); const aiScene *sc = msh->getMesh(); if (sc->mNumMeshes != 1) { std::cout << "ERROR: sc->mNumMeshes = " << sc->mNumMeshes << std::endl; } int midx = 0; // std::cout << "v: " << sc->mMeshes[midx]->mNumVertices << " f: " << sc->mMeshes[midx]->mNumFaces << std::endl; pcl::PointCloud<pcl::PointNormal>::Ptr cloud_1 (new pcl::PointCloud<pcl::PointNormal>); uniform_sampling(sc->mMeshes[midx], 1000000, *cloud_1); for (int pidx = 0; pidx < cloud_1->points.size(); pidx++) { KDL::Vector pt_L = T_L_S * KDL::Vector(cloud_1->points[pidx].x, cloud_1->points[pidx].y, cloud_1->points[pidx].z); cloud_1->points[pidx].x = pt_L.x(); cloud_1->points[pidx].y = pt_L.y(); cloud_1->points[pidx].z = pt_L.z(); } // Voxelgrid boost::shared_ptr<pcl::VoxelGrid<pcl::PointNormal> > grid_(new pcl::VoxelGrid<pcl::PointNormal>); pcl::PointCloud<pcl::PointNormal>::Ptr res(new pcl::PointCloud<pcl::PointNormal>); grid_->setDownsampleAllData(true); grid_->setSaveLeafLayout(true); grid_->setInputCloud(cloud_1); grid_->setLeafSize(0.004, 0.004, 0.004); grid_->filter (*res); point_clouds_map[body_name] = res; frames_map[body_name] = T_W_L; grids_map[body_name] = grid_; std::cout << "res->points.size(): " << res->points.size() << std::endl; pcl::search::KdTree<pcl::PointNormal>::Ptr tree (new pcl::search::KdTree<pcl::PointNormal>); // Setup the principal curvatures computation pcl::PrincipalCurvaturesEstimation<pcl::PointNormal, pcl::PointNormal, pcl::PrincipalCurvatures> principalCurvaturesEstimation; // Provide the original point cloud (without normals) principalCurvaturesEstimation.setInputCloud (res); // Provide the point cloud with normals principalCurvaturesEstimation.setInputNormals(res); // Use the same KdTree from the normal estimation principalCurvaturesEstimation.setSearchMethod (tree); principalCurvaturesEstimation.setRadiusSearch(0.02); // Actually compute the principal curvatures pcl::PointCloud<pcl::PrincipalCurvatures>::Ptr principalCurvatures (new pcl::PointCloud<pcl::PrincipalCurvatures> ()); principalCurvaturesEstimation.compute (*principalCurvatures); point_pc_clouds_map[body_name] = principalCurvatures; features_map[body_name].reset( new std::vector<KDL::Frame >(res->points.size()) ); for (int pidx = 0; pidx < res->points.size(); pidx++) { KDL::Vector nx, ny, nz(res->points[pidx].normal[0], res->points[pidx].normal[1], res->points[pidx].normal[2]); if ( std::fabs( principalCurvatures->points[pidx].pc1 - principalCurvatures->points[pidx].pc2 ) > 0.001) { nx = KDL::Vector(principalCurvatures->points[pidx].principal_curvature[0], principalCurvatures->points[pidx].principal_curvature[1], principalCurvatures->points[pidx].principal_curvature[2]); } else { if (std::fabs(nz.z()) < 0.7) { nx = KDL::Vector(0, 0, 1); } else { nx = KDL::Vector(1, 0, 0); } } ny = nz * nx; nx = ny * nz; nx.Normalize(); ny.Normalize(); nz.Normalize(); (*features_map[body_name])[pidx] = KDL::Frame( KDL::Rotation(nx, ny, nz), KDL::Vector(res->points[pidx].x, res->points[pidx].y, res->points[pidx].z) ); } } } } } const double sigma_p = 0.01;//05; const double sigma_q = 10.0/180.0*PI;//100.0; const double sigma_r = 0.2;//05; double sigma_c = 5.0/180.0*PI; int m_id = 101; // generate object model boost::shared_ptr<ObjectModel > om(new ObjectModel); for (int pidx = 0; pidx < point_clouds_map[ob_name]->points.size(); pidx++) { if (point_pc_clouds_map[ob_name]->points[pidx].pc1 > 1.1 * point_pc_clouds_map[ob_name]->points[pidx].pc2) { // e.g. pc1=1, pc2=0 // edge om->addPointFeature((*features_map[ob_name])[pidx] * KDL::Frame(KDL::Rotation::RotZ(PI)), point_pc_clouds_map[ob_name]->points[pidx].pc1, point_pc_clouds_map[ob_name]->points[pidx].pc2); om->addPointFeature((*features_map[ob_name])[pidx], point_pc_clouds_map[ob_name]->points[pidx].pc1, point_pc_clouds_map[ob_name]->points[pidx].pc2); } else { for (double angle = 0.0; angle < 359.0/180.0*PI; angle += 20.0/180.0*PI) { om->addPointFeature((*features_map[ob_name])[pidx] * KDL::Frame(KDL::Rotation::RotZ(angle)), point_pc_clouds_map[ob_name]->points[pidx].pc1, point_pc_clouds_map[ob_name]->points[pidx].pc2); } } } std::cout << "om.getPointFeatures().size(): " << om->getPointFeatures().size() << std::endl; KDL::Frame T_W_O = frames_map[ob_name]; // generate collision model std::map<std::string, std::list<std::pair<int, double> > > link_pt_map; boost::shared_ptr<CollisionModel > cm(new CollisionModel); cm->setSamplerParameters(sigma_p, sigma_q, sigma_r); std::list<std::string > gripper_link_names; for (int bidx = 0; bidx < bh->getNumBodyNodes(); bidx++) { const std::string &link_name = bh->getBodyNode(bidx)->getName(); gripper_link_names.push_back(link_name); } double dist_range = 0.01; for (std::list<std::string >::const_iterator nit = gripper_link_names.begin(); nit != gripper_link_names.end(); nit++) { const std::string &link_name = (*nit); if (point_clouds_map.find( link_name ) == point_clouds_map.end()) { continue; } cm->addLinkContacts(dist_range, link_name, point_clouds_map[link_name], frames_map[link_name], om->getPointFeatures(), T_W_O); } // generate hand configuration model boost::shared_ptr<HandConfigurationModel > hm(new HandConfigurationModel); std::map<std::string, double> joint_q_map_before( joint_q_map ); double angleDiffKnuckleTwo = 15.0/180.0*PI; joint_q_map_before["right_HandFingerOneKnuckleTwoJoint"] -= angleDiffKnuckleTwo; joint_q_map_before["right_HandFingerTwoKnuckleTwoJoint"] -= angleDiffKnuckleTwo; joint_q_map_before["right_HandFingerThreeKnuckleTwoJoint"] -= angleDiffKnuckleTwo; joint_q_map_before["right_HandFingerOneKnuckleThreeJoint"] -= angleDiffKnuckleTwo*0.333333; joint_q_map_before["right_HandFingerTwoKnuckleThreeJoint"] -= angleDiffKnuckleTwo*0.333333; joint_q_map_before["right_HandFingerThreeKnuckleThreeJoint"] -= angleDiffKnuckleTwo*0.333333; hm->generateModel(joint_q_map_before, joint_q_map, 1.0, 10, sigma_c); writeToXml(argv[3], cm, hm); return 0; }
// Add all methods from the provides list of the given entity into lists in the // given symbol table. static bool collate_provided(ast_t* entity, methods_t* method_info) { assert(entity != NULL); bool r = true; ast_t* traits = ast_childidx(entity, 3); for(ast_t* t = ast_child(traits); t != NULL; t = ast_sibling(t)) { assert(ast_id(t) == TK_NOMINAL); ast_t* trait_def = (ast_t*)ast_data(t); assert(trait_def != NULL); // TODO: Check whether we need an error here if((ast_id(trait_def) != TK_TRAIT) && (ast_id(trait_def) != TK_INTERFACE)) return false; // Check for duplicates in our provides list // This is just simple compare of each entry against all the other. This is // clearly O(n^2), but since provides lists are likely to be small that // should be OK. If it turns out to be a problem it can be changed later. for(ast_t* p = ast_child(traits); p != t; p = ast_sibling(p)) { if(trait_def == (ast_t*)ast_data(p)) { ast_error(t, "duplicate entry in provides list"); ast_error(p, "previous entry here"); } } if(!build_entity_def(trait_def)) return false; ast_t* type_params = ast_childidx(trait_def, 1); ast_t* type_args = ast_childidx(t, 2); ast_t* trait_methods = ast_childidx(trait_def, 4); for(ast_t* m = ast_child(trait_methods); m != NULL; m = ast_sibling(m)) { // Reify the method with the type parameters from trait definition and // the reified type arguments from trait reference ast_t* r_method = reify(m, type_params, type_args); const char* entity_name = ast_name(ast_child(entity)); if(ast_id(r_method) == TK_BE || ast_id(r_method) == TK_NEW) { // Modify return type to the inheritting type ast_t* ret_type = ast_childidx(r_method, 4); assert(ast_id(ret_type) == TK_NOMINAL); const char* pkg_name = package_name(ast_nearest(entity, TK_PACKAGE)); ast_set_name(ast_childidx(ret_type, 0), pkg_name); ast_set_name(ast_childidx(ret_type, 1), entity_name); } if(!add_method_to_list(r_method, method_info, entity_name)) r = false; } } return r; }
/** \brief Parse a line from a version entry. * * This function reads the header (one line), a list of logs, and then * the footer of a version entry. * * If a version exists, even if it is wrong, the function returns true. * The function returns false if the end of the file is reached. * * \param[in] s The state to read from. * * \return true if the end of the file was not yet reached when this * version was read in full. */ bool changelog_file::version::parse(state& s) { f_filename = s.get_filename(); f_line = s.get_line(); // the current line must be the header if(s.space_count() != 0) { wpkg_output::log("changelog:%1:%2: a changelog version entry must start with a valid header") .arg(f_filename) .arg(f_line) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .action("changelog"); return s.next_line(); } bool good_header(true); const std::string& header(s.last_line()); const char *h(header.c_str()); const char *start(h); // *** Package Name *** for(; !isspace(*h) && *h != '('; ++h) { if(*h == '\0') { wpkg_output::log("changelog:%1:%2: invalid header, expected the project name, version, distributions, and urgency information") .arg(f_filename) .arg(f_line) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .action("changelog"); good_header = false; break; } } if(good_header) { std::string package_name(start, h - start); if(!wpkg_util::is_package_name(package_name)) { wpkg_output::log("changelog:%1:%2: the package name %3 is not valid") .arg(f_filename) .arg(f_line) .quoted_arg(package_name) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .action("changelog"); } else { f_package = package_name; } if(!isspace(*h)) { // this is just a warning, but the user is expected to put a space // after the package name and before the version wpkg_output::log("changelog:%1:%2: the package name %3 is not followed by a space before the version information") .arg(f_filename) .arg(f_line) .quoted_arg(package_name) .level(wpkg_output::level_warning) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_header = false; } } // *** Version *** if(good_header) { for(; isspace(*h); ++h); if(*h != '(') { wpkg_output::log("changelog:%1:%2: invalid header, expected the version between parenthesis after the package name") .arg(f_filename) .arg(f_line) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_header = false; } } if(good_header) { // read the version ++h; start = h; for(; !isspace(*h) && *h != ')' && *h != '\0'; ++h); std::string version_str(start, h - start); char err[256]; if(!validate_debian_version(version_str.c_str(), err, sizeof(err))) { wpkg_output::log("control:%1:%2: %3 is not a valid Debian version") .arg(f_filename) .arg(f_line) .quoted_arg(version_str) .level(wpkg_output::level_error) .module(wpkg_output::module_changelog) .package(f_package) .action("changelog"); good_header = false; } else { f_version = version_str; } } if(good_header) { if(isspace(*h)) { for(; isspace(*h); ++h); wpkg_output::log("control:%1:%2: version %3 is not immediately followed by a closing parenthesis") .arg(f_filename) .arg(f_line) .quoted_arg(f_version) .level(wpkg_output::level_warning) .module(wpkg_output::module_changelog) .package(f_package) .action("changelog"); } if(*h != ')') { wpkg_output::log("control:%1:%2: version %3 is not followed by a closing parenthesis: ')'") .arg(f_filename) .arg(f_line) .quoted_arg(f_version) .level(wpkg_output::level_error) .module(wpkg_output::module_changelog) .package(f_package) .action("changelog"); good_header = false; } } // *** Distributions *** if(good_header) { // the first ++h is to skip the ')' from the version for(++h; isspace(*h); ++h); do { start = h; for(; !isspace(*h) && *h != '\0' && *h != ';' && *h != ','; ++h); std::string d(start, h - start); wpkg_filename::uri_filename distribution(d); if(distribution.is_absolute()) { wpkg_output::log("control:%1:%2: a distribution must be a relative path, %3 is not acceptable") .arg(f_filename) .arg(f_line) .quoted_arg(distribution) .level(wpkg_output::level_error) .module(wpkg_output::module_changelog) .package(f_package) .action("changelog"); good_header = false; } else if(distribution.segment_size() < 1) { // This happens if no distribution is specified wpkg_output::log("control:%1:%2: a distribution cannot be the empty string") .arg(f_filename) .arg(f_line) .level(wpkg_output::level_error) .module(wpkg_output::module_changelog) .package(f_package) .action("changelog"); good_header = false; } else { f_distributions.push_back(distribution.original_filename()); } for(; isspace(*h); ++h); } while(*h != '\0' && *h != ';' && *h != ','); } // We want to support more than one in source packages, that way a package // can be part of stable and unstable (because we view our distributions // as separate entities rather than complements.) //if(good_header) //{ // if(f_distributions.size() > 1) // { // wpkg_output::log("control:%1:%2: although the syntax allows for more than one distribution, we do not support more than one at this time") // .arg(f_filename) // .arg(f_line) // .level(wpkg_output::level_error) // .module(wpkg_output::module_changelog) // .package(f_package) // .action("changelog"); // good_header = false; // } //} // *** Parameters *** if(good_header) { for(; isspace(*h); ++h); if(*h != ';') { wpkg_output::log("changelog:%1:%2: invalid header, expected the list of distributions to end with ';'") .arg(f_filename) .arg(f_line) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_header = false; } else { for(++h; isspace(*h); ++h); } } while(good_header && *h != '\0') { start = h; const char *e(h); const char *equal(NULL); for(; *h != ',' && *h != '\0'; ++h) { if(!isspace(*h)) { e = h + 1; } if(*h == '=') { equal = h; } } if(equal == NULL) { wpkg_output::log("changelog:%1:%2: invalid header, parameter %3 is expected to include an equal sign (=) after the parameter name") .arg(f_filename) .arg(f_line) .quoted_arg(std::string(start, h - start)) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_header = false; } else if(start == equal) { wpkg_output::log("changelog:%1:%2: invalid header, parameter %3 is missing a name before the equal character") .arg(f_filename) .arg(f_line) .quoted_arg(std::string(start, h - start)) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_header = false; } else { std::string name(start, equal - start); if(f_parameters.find(name) != f_parameters.end()) { wpkg_output::log("changelog:%1:%2: invalid header, parameter %3 is defined twice") .arg(f_filename) .arg(f_line) .quoted_arg(name) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_header = false; } else { ++equal; std::string value(equal, e - equal); f_parameters[name] = value; } } // skip commas and spaces and repeat for next parameter for(; *h == ',' || isspace(*h); ++h); } // We got the header, now we check for the list of logs // the log::parse() function is expected to return false // once we reach the end of that list, then we must find // a footer that starts with a space and two dashes if(!s.next_line()) { wpkg_output::log("changelog:%1:%2: every changelog version entry must have at least one log and end with a valid footer") .arg(f_filename) .arg(s.get_line()) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); return false; } bool group(true); for(;;) { log l; if(!l.parse(s, group)) { break; } f_logs.push_back(l); } // we are at the end of the log stream for this version entry, // there has to be a valid footer now bool good_footer(true); if(s.space_count() != 1) { wpkg_output::log("changelog:%1:%2: a changelog version entry must end with a valid footer, which must start with exactly one space") .arg(f_filename) .arg(s.get_line()) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_footer = false; } const std::string& footer(s.last_line()); const char *f(footer.c_str()); if(good_footer) { if(f[0] != '-' || f[1] != '-' || f[2] != ' ') { wpkg_output::log("changelog:%1:%2: a changelog version entry must end with a valid footer, which must start with two dashes") .arg(f_filename) .arg(s.get_line()) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_footer = false; } } if(good_footer) { // search for two spaces to break the maintainer name/email and date f += 3; start = f; for(; f[0] != '\0' && (f[0] != ' ' || f[1] != ' '); ++f); std::string maintainer(start, f - start); // TODO: use libtld to verify email f_maintainer = maintainer; std::string date(f + 2); struct tm time_info; if(strptime(date.c_str(), "%a, %d %b %Y %H:%M:%S %z", &time_info) == NULL) { wpkg_output::log("changelog:%1:%2: the footer in this changelog version entry has an invalid date: %3") .arg(f_filename) .arg(s.get_line()) .quoted_arg(date) .level(wpkg_output::level_error) .module(wpkg_output::module_build_package) .package(f_package) .action("changelog"); good_footer = false; } else { f_date = date; } } // do not read another line in case this one is the next header return true; }