bool RClient::parse(int &argc, char **argv) { Rct::findExecutablePath(*argv); mSocketFile = Path::home() + ".rdm"; List<option> options; options.reserve(sizeof(opts) / sizeof(Option)); List<std::shared_ptr<QueryCommand> > projectCommands; String shortOptionString; Hash<int, Option*> shortOptions, longOptions; for (int i=0; opts[i].description; ++i) { if (opts[i].option != None) { const option opt = { opts[i].longOpt, opts[i].argument, 0, opts[i].shortOpt }; if (opts[i].shortOpt) { shortOptionString.append(opts[i].shortOpt); switch (opts[i].argument) { case no_argument: break; case required_argument: shortOptionString.append(':'); break; case optional_argument: shortOptionString.append("::"); break; } assert(!shortOptions.contains(opts[i].shortOpt)); shortOptions[opts[i].shortOpt] = &opts[i]; } if (opts[i].longOpt) longOptions[options.size()] = &opts[i]; options.push_back(opt); } } if (getenv("RTAGS_DUMP_UNUSED")) { String unused; for (int i=0; i<26; ++i) { if (!shortOptionString.contains('a' + i)) unused.append('a' + i); if (!shortOptionString.contains('A' + i)) unused.append('A' + i); } printf("Unused: %s\n", unused.constData()); for (int i=0; opts[i].description; ++i) { if (opts[i].longOpt) { if (!opts[i].shortOpt) { printf("No shortoption for %s\n", opts[i].longOpt); } else if (opts[i].longOpt[0] != opts[i].shortOpt) { printf("Not ideal option for %s|%c\n", opts[i].longOpt, opts[i].shortOpt); } } } return 0; } { const option opt = { 0, 0, 0, 0 }; options.push_back(opt); } Path logFile; unsigned logFlags = 0; enum State { Parsing, Done, Error } state = Parsing; while (true) { int idx = -1; const int c = getopt_long(argc, argv, shortOptionString.constData(), options.data(), &idx); switch (c) { case -1: state = Done; break; case '?': case ':': state = Error; break; default: break; } if (state != Parsing) break; const Option *opt = (idx == -1 ? shortOptions.value(c) : longOptions.value(idx)); assert(opt); switch (opt->option) { case None: assert(0); break; case Help: help(stdout, argv[0]); return 0; case Man: man(); return 0; case SocketFile: mSocketFile = optarg; break; case IMenu: mQueryFlags |= QueryMessage::IMenu; break; case ContainingFunction: mQueryFlags |= QueryMessage::ContainingFunction; break; case DeclarationOnly: mQueryFlags |= QueryMessage::DeclarationOnly; break; case FindVirtuals: mQueryFlags |= QueryMessage::FindVirtuals; break; case FindFilePreferExact: mQueryFlags |= QueryMessage::FindFilePreferExact; break; case CursorInfoIncludeParents: mQueryFlags |= QueryMessage::CursorInfoIncludeParents; break; case CursorInfoIncludeTargets: mQueryFlags |= QueryMessage::CursorInfoIncludeTargets; break; case CursorInfoIncludeReferences: mQueryFlags |= QueryMessage::CursorInfoIncludeReferences; break; case CursorKind: mQueryFlags |= QueryMessage::CursorKind; break; case CodeComplete: // logFile = "/tmp/rc.log"; mCommands.append(std::shared_ptr<RCCommand>(new CompletionCommand)); break; case Context: mContext = optarg; break; case CodeCompleteAt: { const String arg = optarg; List<RegExp::Capture> caps; RegExp rx("^\\(.*\\):\\([0-9][0-9]*\\):\\([0-9][0-9]*\\)$"); if (rx.indexIn(arg, 0, &caps) != 0 || caps.size() != 4) { fprintf(stderr, "Can't decode argument for --code-complete-at [%s]\n", optarg); return false; } const Path path = Path::resolved(caps[1].capture, Path::MakeAbsolute); if (!path.exists()) { fprintf(stderr, "Can't decode argument for --code-complete-at [%s]\n", optarg); return false; } String out; { Serializer serializer(out); serializer << path << atoi(caps[2].capture.constData()) << atoi(caps[3].capture.constData()); } CompletionCommand *cmd = new CompletionCommand(path, atoi(caps[2].capture.constData()), atoi(caps[3].capture.constData())); mCommands.append(std::shared_ptr<RCCommand>(cmd)); break; } case DisplayName: mQueryFlags |= QueryMessage::DisplayName; break; case AllReferences: mQueryFlags |= QueryMessage::AllReferences; break; case MatchCaseInsensitive: mQueryFlags |= QueryMessage::MatchCaseInsensitive; break; case MatchRegexp: mQueryFlags |= QueryMessage::MatchRegexp; break; case AbsolutePath: mQueryFlags |= QueryMessage::AbsolutePath; break; case ReverseSort: mQueryFlags |= QueryMessage::ReverseSort; break; case ElispList: mQueryFlags |= QueryMessage::ElispList; break; case FilterSystemHeaders: mQueryFlags |= QueryMessage::FilterSystemIncludes; break; case NoContext: mQueryFlags |= QueryMessage::NoContext; break; case PathFilter: { Path p = optarg; p.resolve(); mPathFilters.insert(p); break; } case RangeFilter: { List<RegExp::Capture> caps; RegExp rx("^\\([0-9][0-9]*\\)-\\([0-9][0-9]*\\)$"); if (rx.indexIn(optarg, 0, &caps) != 0 || caps.size() != 3) { fprintf(stderr, "Can't parse range, must be uint-uint. E.g. 1-123\n"); return false; } else { mMinOffset = atoi(caps.at(1).capture.constData()); mMaxOffset = atoi(caps.at(2).capture.constData()); if (mMaxOffset <= mMinOffset || mMinOffset < 0) { fprintf(stderr, "Invalid range (%d-%d), must be uint-uint. E.g. 1-123\n", mMinOffset, mMaxOffset); return false; } } break; } case LineNumbers: mQueryFlags |= QueryMessage::LineNumbers; break; case Verbose: ++mLogLevel; break; case Silent: mLogLevel = -1; break; case LogFile: logFile = optarg; break; case StripParen: mQueryFlags |= QueryMessage::StripParentheses; break; case ConnectTimeout: mConnectTimeout = atoi(optarg); if (mConnectTimeout < 0) { fprintf(stderr, "--connect-timeout [arg] must be >= 0\n"); return false; } break; case Max: mMax = atoi(optarg); if (mMax <= 0) { fprintf(stderr, "-M [arg] must be positive integer\n"); return false; } break; case Timeout: mTimeout = atoi(optarg); if (mTimeout <= 0) { fprintf(stderr, "-y [arg] must be positive integer\n"); return false; } break; case UnsavedFile: { const String arg(optarg); const int colon = arg.lastIndexOf(':'); if (colon == -1) { fprintf(stderr, "Can't parse -u [%s]\n", optarg); return false; } const int bytes = atoi(arg.constData() + colon + 1); if (!bytes) { fprintf(stderr, "Can't parse -u [%s]\n", optarg); return false; } const Path path = Path::resolved(arg.left(colon), Path::MakeAbsolute); if (!path.isFile()) { fprintf(stderr, "Can't open [%s] for reading\n", arg.left(colon).nullTerminated()); return false; } String contents(bytes, '\0'); const int r = fread(contents.data(), 1, bytes, stdin); if (r != bytes) { fprintf(stderr, "Read error %d (%s). Got %d, expected %d\n", errno, strerror(errno), r, bytes); return false; } mUnsavedFiles[path] = contents; break; } case FollowLocation: case CursorInfo: case ReferenceLocation: { const String encoded = Location::encodeClientLocation(optarg); if (encoded.isEmpty()) { fprintf(stderr, "Can't resolve argument %s\n", optarg); return false; } QueryMessage::Type type = QueryMessage::Invalid; switch (opt->option) { case FollowLocation: type = QueryMessage::FollowLocation; break; case CursorInfo: type = QueryMessage::CursorInfo; break; case ReferenceLocation: type = QueryMessage::ReferencesLocation; break; default: assert(0); break; } addQuery(type, encoded); break; } case WithProject: mProjects.append(optarg); break; case ReloadFileManager: addQuery(QueryMessage::ReloadFileManager); break; case ReloadProjects: addQuery(QueryMessage::ReloadProjects); break; case Clear: addQuery(QueryMessage::ClearProjects); break; case RdmLog: addLog(RdmLogCommand::Default); break; case Diagnostics: addLog(RTags::CompilationError); break; case XmlDiagnostics: addLog(RTags::CompilationErrorXml); break; case QuitRdm: addQuery(QueryMessage::Shutdown); break; case DeleteProject: addQuery(QueryMessage::DeleteProject, optarg); break; case CodeCompletionEnabled: addQuery(QueryMessage::CodeCompletionEnabled); break; case UnloadProject: addQuery(QueryMessage::UnloadProject, optarg); break; case FindProjectRoot: { const Path p = Path::resolved(optarg); printf("findProjectRoot [%s] => [%s]\n", p.constData(), RTags::findProjectRoot(p).constData()); return 0; } case Reindex: case Project: case FindFile: case ListSymbols: case FindSymbols: case JSON: case Builds: case JobCount: case Status: { QueryMessage::Type type = QueryMessage::Invalid; switch (opt->option) { case Reindex: type = QueryMessage::Reindex; break; case Project: type = QueryMessage::Project; break; case FindFile: type = QueryMessage::FindFile; break; case Builds: type = QueryMessage::Builds; break; case Status: type = QueryMessage::Status; break; case JSON: type = QueryMessage::JSON; break; case ListSymbols: type = QueryMessage::ListSymbols; break; case FindSymbols: type = QueryMessage::FindSymbols; break; case JobCount: type = QueryMessage::JobCount; break; default: assert(0); break; } if (optarg) { addQuery(type, optarg); } else if (optind < argc && argv[optind][0] != '-') { addQuery(type, argv[optind++]); } else { addQuery(type); } assert(!mCommands.isEmpty()); if (type == QueryMessage::Project) projectCommands.append(std::static_pointer_cast<QueryCommand>(mCommands.back())); break; } case LoadCompilationDatabase: { Path dir; if (optarg) { dir = optarg; } else if (optind < argc && argv[optind][0] != '-') { dir = argv[optind++]; } else { dir = Path::pwd(); } dir.resolve(Path::MakeAbsolute); if (!dir.exists()) { fprintf(stderr, "%s does not seem to exist\n", dir.constData()); return false; } if (!dir.isDir()) { fprintf(stderr, "%s is not a directory\n", dir.constData()); return false; } if (!dir.endsWith('/')) dir += '/'; const Path file = dir + "compile_commands.json"; if (!file.isFile()) { fprintf(stderr, "no compile_commands.json file in %s\n", dir.constData()); return false; } addQuery(QueryMessage::LoadCompilationDatabase, dir); break; } case HasFileManager: { Path p; if (optarg) { p = optarg; } else if (optind < argc && argv[optind][0] != '-') { p = argv[optind++]; } else { p = "."; } p.resolve(Path::MakeAbsolute); if (!p.exists()) { fprintf(stderr, "%s does not seem to exist\n", optarg); return false; } if (p.isDir()) p.append('/'); addQuery(QueryMessage::HasFileManager, p); break; } case SuspendFile: { Path p; if (optarg) { p = optarg; } else if (optind < argc && argv[optind][0] != '-') { p = argv[optind++]; } if (!p.isEmpty()) { if (p == "clear" && !p.exists()) { } else { p.resolve(Path::MakeAbsolute); if (!p.isFile() && p != "clear") { fprintf(stderr, "%s is not a file\n", optarg); return false; } } } addQuery(QueryMessage::SuspendFile, p); break; } case Compile: { String args = optarg; while (optind < argc) { args.append(' '); args.append(argv[optind++]); } addCompile(Path::pwd(), args); break; } case IsIndexing: addQuery(QueryMessage::IsIndexing); break; case IsIndexed: case DumpFile: case Dependencies: case FixIts: { Path p = optarg; if (!p.exists()) { fprintf(stderr, "%s does not exist\n", optarg); return false; } if (!p.isAbsolute()) p.prepend(Path::pwd()); if (p.isDir()) { if (opt->option != IsIndexed) { fprintf(stderr, "%s is not a file\n", optarg); return false; } else if (!p.endsWith('/')) { p.append('/'); } } QueryMessage::Type type = QueryMessage::Invalid; switch (opt->option) { case Dependencies: type = QueryMessage::Dependencies; break; case FixIts: type = QueryMessage::FixIts; break; case IsIndexed: type = QueryMessage::IsIndexed; break; case DumpFile: type = QueryMessage::DumpFile; break; default: assert(0); break; } addQuery(type, p); break; } case PreprocessFile: { Path p = optarg; p.resolve(Path::MakeAbsolute); if (!p.isFile()) { fprintf(stderr, "%s is not a file\n", optarg); return false; } addQuery(QueryMessage::PreprocessFile, p); break; } case RemoveFile: { const Path p = Path::resolved(optarg, Path::MakeAbsolute); if (!p.exists()) { addQuery(QueryMessage::RemoveFile, p); } else { addQuery(QueryMessage::RemoveFile, optarg); } break; } case ReferenceName: addQuery(QueryMessage::ReferencesName, optarg); break; } } if (state == Error) { help(stderr, argv[0]); return false; } if (optind < argc) { fprintf(stderr, "rc: unexpected option -- '%s'\n", argv[optind]); return false; } if (!initLogging(argv[0], LogStderr, mLogLevel, logFile, logFlags)) { fprintf(stderr, "Can't initialize logging with %s %d %d %s 0x%0x\n", argv[0], LogStderr, mLogLevel, logFile.constData(), logFlags); return false; } if (mCommands.isEmpty()) { help(stderr, argv[0]); return false; } if (mCommands.size() > projectCommands.size()) { // If there's more than one command one likely does not want output from // the queryCommand (unless there's no arg specified for it). This is so // we don't have to pass a different flag for auto-updating project // using the current buffer but rather piggy-back on --project const int count = projectCommands.size(); for (int i=0; i<count; ++i) { std::shared_ptr<QueryCommand> &cmd = projectCommands[i]; if (!cmd->query.isEmpty()) { cmd->extraQueryFlags |= QueryMessage::Silent; } } } if (!logFile.isEmpty() || mLogLevel > 0) { Log l(1); l << argc; for (int i = 0; i < argc; ++i) l << " " << argv[i]; } mArgc = argc; mArgv = argv; return true; }
RClient::ParseStatus RClient::parse(int &argc, char **argv) { Rct::findExecutablePath(*argv); mSocketFile = Path::home() + ".rdm"; List<option> options; options.reserve(sizeof(opts) / sizeof(Option)); List<std::shared_ptr<QueryCommand> > projectCommands; String shortOptionString; Hash<int, Option*> shortOptions, longOptions; for (int i=0; opts[i].description; ++i) { if (opts[i].option != None) { const option opt = { opts[i].longOpt, opts[i].argument, 0, opts[i].shortOpt }; if (opts[i].shortOpt) { shortOptionString.append(opts[i].shortOpt); switch (opts[i].argument) { case no_argument: break; case required_argument: shortOptionString.append(':'); break; case optional_argument: shortOptionString.append("::"); break; } assert(!shortOptions.contains(opts[i].shortOpt)); shortOptions[opts[i].shortOpt] = &opts[i]; } if (opts[i].longOpt) longOptions[options.size()] = &opts[i]; options.push_back(opt); } } if (getenv("RTAGS_DUMP_UNUSED")) { String unused; for (int i=0; i<26; ++i) { if (!shortOptionString.contains('a' + i)) unused.append('a' + i); if (!shortOptionString.contains('A' + i)) unused.append('A' + i); } printf("Unused: %s\n", unused.constData()); for (int i=0; opts[i].description; ++i) { if (opts[i].longOpt) { if (!opts[i].shortOpt) { printf("No shortoption for %s\n", opts[i].longOpt); } else if (opts[i].longOpt[0] != opts[i].shortOpt) { printf("Not ideal option for %s|%c\n", opts[i].longOpt, opts[i].shortOpt); } } } return Parse_Ok; } { const option opt = { 0, 0, 0, 0 }; options.push_back(opt); } Path logFile; Flags<LogFileFlag> logFlags; enum State { Parsing, Done, Error } state = Parsing; while (true) { int idx = -1; const int c = getopt_long(argc, argv, shortOptionString.constData(), options.data(), &idx); switch (c) { case -1: state = Done; break; case '?': case ':': state = Error; break; default: break; } if (state != Parsing) break; const Option *opt = (idx == -1 ? shortOptions.value(c) : longOptions.value(idx)); assert(opt); if (!isatty(STDOUT_FILENO)) { mQueryFlags |= QueryMessage::NoColor; } switch (opt->option) { case None: case NumOptions: assert(0); break; case Help: help(stdout, argv[0]); return Parse_Ok; case Man: man(); return Parse_Ok; case SocketFile: mSocketFile = optarg; break; case GuessFlags: mGuessFlags = true; break; case Wait: mQueryFlags |= QueryMessage::Wait; break; case IMenu: mQueryFlags |= QueryMessage::IMenu; break; case CompilationFlagsOnly: mQueryFlags |= QueryMessage::CompilationFlagsOnly; break; case NoColor: mQueryFlags |= QueryMessage::NoColor; break; case CompilationFlagsSplitLine: mQueryFlags |= QueryMessage::CompilationFlagsSplitLine; break; case ContainingFunction: mQueryFlags |= QueryMessage::ContainingFunction; break; case ContainingFunctionLocation: mQueryFlags |= QueryMessage::ContainingFunctionLocation; break; case DeclarationOnly: mQueryFlags |= QueryMessage::DeclarationOnly; break; case DefinitionOnly: mQueryFlags |= QueryMessage::DefinitionOnly; break; case FindVirtuals: mQueryFlags |= QueryMessage::FindVirtuals; break; case FindFilePreferExact: mQueryFlags |= QueryMessage::FindFilePreferExact; break; case SymbolInfoIncludeParents: mQueryFlags |= QueryMessage::SymbolInfoIncludeParents; break; case SymbolInfoExcludeTargets: mQueryFlags |= QueryMessage::SymbolInfoExcludeTargets; break; case SymbolInfoExcludeReferences: mQueryFlags |= QueryMessage::SymbolInfoExcludeReferences; break; case CursorKind: mQueryFlags |= QueryMessage::CursorKind; break; case SynchronousCompletions: mQueryFlags |= QueryMessage::SynchronousCompletions; break; case DisplayName: mQueryFlags |= QueryMessage::DisplayName; break; case AllReferences: mQueryFlags |= QueryMessage::AllReferences; break; case AllTargets: mQueryFlags |= QueryMessage::AllTargets; break; case MatchCaseInsensitive: mQueryFlags |= QueryMessage::MatchCaseInsensitive; break; case MatchRegex: mQueryFlags |= QueryMessage::MatchRegex; break; case AbsolutePath: mQueryFlags |= QueryMessage::AbsolutePath; break; case ReverseSort: mQueryFlags |= QueryMessage::ReverseSort; break; case Rename: mQueryFlags |= QueryMessage::Rename; break; case Elisp: mQueryFlags |= QueryMessage::Elisp; break; case FilterSystemHeaders: mQueryFlags |= QueryMessage::FilterSystemIncludes; break; case NoContext: mQueryFlags |= QueryMessage::NoContext; break; case PathFilter: { Path p = optarg; p.resolve(); mPathFilters.insert({ p, QueryMessage::PathFilter::Self }); break; } case DependencyFilter: { Path p = optarg; p.resolve(); if (!p.isFile()) { fprintf(stderr, "%s doesn't seem to be a file\n", optarg); return Parse_Error; } mPathFilters.insert({ p, QueryMessage::PathFilter::Dependency }); break; } case KindFilter: mKindFilters.insert(optarg); break; case WildcardSymbolNames: mQueryFlags |= QueryMessage::WildcardSymbolNames; break; case RangeFilter: { char *end; mMinOffset = strtoul(optarg, &end, 10); if (*end != '-') { fprintf(stderr, "Can't parse range, must be uint-uint. E.g. 1-123\n"); return Parse_Error; } mMaxOffset = strtoul(end + 1, &end, 10); if (*end) { fprintf(stderr, "Can't parse range, must be uint-uint. E.g. 1-123\n"); return Parse_Error; } if (mMaxOffset <= mMinOffset || mMinOffset < 0) { fprintf(stderr, "Invalid range (%d-%d), must be uint-uint. E.g. 1-123\n", mMinOffset, mMaxOffset); return Parse_Error; } break; } case Version: fprintf(stdout, "%s\n", RTags::versionString().constData()); return Parse_Ok; case Verbose: ++mLogLevel; break; case PrepareCodeCompleteAt: case CodeCompleteAt: { const String encoded = Location::encode(optarg); if (encoded.isEmpty()) { fprintf(stderr, "Can't resolve argument %s\n", optarg); return Parse_Error; } addQuery(opt->option == CodeCompleteAt ? QueryMessage::CodeCompleteAt : QueryMessage::PrepareCodeCompleteAt, encoded); break; } case Silent: mLogLevel = LogLevel::None; break; case LogFile: logFile = optarg; break; case StripParen: mQueryFlags |= QueryMessage::StripParentheses; break; case DumpIncludeHeaders: mQueryFlags |= QueryMessage::DumpIncludeHeaders; break; case SilentQuery: mQueryFlags |= QueryMessage::SilentQuery; break; case BuildIndex: { bool ok; mBuildIndex = String(optarg).toULongLong(&ok); if (!ok) { fprintf(stderr, "--build-index [arg] must be >= 0\n"); return Parse_Error; } break; } case ConnectTimeout: mConnectTimeout = atoi(optarg); if (mConnectTimeout < 0) { fprintf(stderr, "--connect-timeout [arg] must be >= 0\n"); return Parse_Error; } break; case Max: mMax = atoi(optarg); if (mMax < 0) { fprintf(stderr, "-M [arg] must be >= 0\n"); return Parse_Error; } break; case Timeout: mTimeout = atoi(optarg); if (!mTimeout) { mTimeout = -1; } else if (mTimeout < 0) { fprintf(stderr, "-y [arg] must be >= 0\n"); return Parse_Error; } break; case UnsavedFile: { const String arg(optarg); const int colon = arg.lastIndexOf(':'); if (colon == -1) { fprintf(stderr, "Can't parse -u [%s]\n", optarg); return Parse_Error; } const int bytes = atoi(arg.constData() + colon + 1); if (!bytes) { fprintf(stderr, "Can't parse -u [%s]\n", optarg); return Parse_Error; } const Path path = Path::resolved(arg.left(colon)); if (!path.isFile()) { fprintf(stderr, "Can't open [%s] for reading\n", arg.left(colon).nullTerminated()); return Parse_Error; } String contents(bytes, '\0'); const int r = fread(contents.data(), 1, bytes, stdin); if (r != bytes) { fprintf(stderr, "Read error %d (%s). Got %d, expected %d\n", errno, Rct::strerror(errno).constData(), r, bytes); return Parse_Error; } mUnsavedFiles[path] = contents; break; } case FollowLocation: case SymbolInfo: case ClassHierarchy: case ReferenceLocation: { const String encoded = Location::encode(optarg); if (encoded.isEmpty()) { fprintf(stderr, "Can't resolve argument %s\n", optarg); return Parse_Error; } QueryMessage::Type type = QueryMessage::Invalid; switch (opt->option) { case FollowLocation: type = QueryMessage::FollowLocation; break; case SymbolInfo: type = QueryMessage::SymbolInfo; break; case ReferenceLocation: type = QueryMessage::ReferencesLocation; break; case ClassHierarchy: type = QueryMessage::ClassHierarchy; break; default: assert(0); break; } addQuery(type, encoded, QueryMessage::HasLocation); break; } case CurrentFile: mCurrentFile.append(Path::resolved(optarg)); break; case ReloadFileManager: addQuery(QueryMessage::ReloadFileManager); break; case DumpCompletions: addQuery(QueryMessage::DumpCompletions); break; case DumpCompilationDatabase: addQuery(QueryMessage::DumpCompilationDatabase); break; case Clear: addQuery(QueryMessage::ClearProjects); break; case RdmLog: addLog(RdmLogCommand::Default); break; case Diagnostics: addLog(RTags::Diagnostics); break; case QuitRdm: { const char *arg = 0; if (optarg) { arg = optarg; } else if (optind < argc && argv[optind][0] != '-') { arg = argv[optind++]; } int exit = 0; if (arg) { bool ok; exit = String(arg).toLongLong(&ok); if (!ok) { fprintf(stderr, "Invalid argument to -q\n"); return Parse_Error; } } addQuitCommand(exit); break; } case DeleteProject: addQuery(QueryMessage::DeleteProject, optarg); break; case SendDiagnostics: addQuery(QueryMessage::SendDiagnostics, optarg); break; case FindProjectRoot: { const Path p = Path::resolved(optarg); printf("findProjectRoot [%s] => [%s]\n", p.constData(), RTags::findProjectRoot(p, RTags::SourceRoot).constData()); return Parse_Ok; } case FindProjectBuildRoot: { const Path p = Path::resolved(optarg); printf("findProjectRoot [%s] => [%s]\n", p.constData(), RTags::findProjectRoot(p, RTags::BuildRoot).constData()); return Parse_Ok; } case RTagsConfig: { const Path p = Path::resolved(optarg); Map<String, String> config = RTags::rtagsConfig(p); printf("rtags-config: %s:\n", p.constData()); for (const auto &it : config) { printf("%s: \"%s\"\n", it.first.constData(), it.second.constData()); } return Parse_Ok; } case CurrentProject: addQuery(QueryMessage::Project, String(), QueryMessage::CurrentProjectOnly); break; case CheckReindex: case Reindex: case Project: case FindFile: case ListSymbols: case FindSymbols: case Sources: case IncludeFile: case JobCount: case Status: { Flags<QueryMessage::Flag> extraQueryFlags; QueryMessage::Type type = QueryMessage::Invalid; bool resolve = true; switch (opt->option) { case CheckReindex: type = QueryMessage::CheckReindex; break; case Reindex: type = QueryMessage::Reindex; break; case Project: type = QueryMessage::Project; break; case FindFile: type = QueryMessage::FindFile; resolve = false; break; case Sources: type = QueryMessage::Sources; break; case IncludeFile: type = QueryMessage::IncludeFile; resolve = false; break; case Status: type = QueryMessage::Status; break; case ListSymbols: type = QueryMessage::ListSymbols; break; case FindSymbols: type = QueryMessage::FindSymbols; break; case JobCount: type = QueryMessage::JobCount; break; default: assert(0); break; } const char *arg = 0; if (optarg) { arg = optarg; } else if (optind < argc && argv[optind][0] != '-') { arg = argv[optind++]; } if (arg) { Path p(arg); if (resolve && p.exists()) { p.resolve(); addQuery(type, p, extraQueryFlags); } else { addQuery(type, arg, extraQueryFlags); } } else { addQuery(type, String(), extraQueryFlags); } assert(!mCommands.isEmpty()); if (type == QueryMessage::Project) projectCommands.append(std::static_pointer_cast<QueryCommand>(mCommands.back())); break; } case ListBuffers: addQuery(QueryMessage::SetBuffers); break; case SetBuffers: { const char *arg = 0; if (optarg) { arg = optarg; } else if (optind < argc && (argv[optind][0] != '-' || !strcmp(argv[optind], "-"))) { arg = argv[optind++]; } String encoded; if (arg) { List<Path> paths; auto addBuffer = [&paths](const String &p) { if (p.isEmpty()) return; Path path(p); if (path.resolve() && path.isFile()) { paths.append(path); } else { fprintf(stderr, "\"%s\" doesn't seem to be a file.\n", p.constData()); } }; if (!strcmp(arg, "-")) { char buf[1024]; while (fgets(buf, sizeof(buf), stdin)) { String arg(buf); if (arg.endsWith('\n')) arg.chop(1); addBuffer(arg); } } else { for (const String &buffer : String(arg).split(';')) { addBuffer(buffer); } } Serializer serializer(encoded); serializer << paths; } addQuery(QueryMessage::SetBuffers, encoded); break; } case LoadCompilationDatabase: { #if CLANG_VERSION_MAJOR > 3 || (CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR > 3) Path dir; if (optarg) { dir = optarg; } else if (optind < argc && argv[optind][0] != '-') { dir = argv[optind++]; } else { dir = Path::pwd(); } dir.resolve(Path::MakeAbsolute); if (!dir.exists()) { fprintf(stderr, "%s does not seem to exist\n", dir.constData()); return Parse_Error; } if (!dir.isDir()) { if (dir.isFile() && dir.endsWith("/compile_commands.json")) { dir = dir.parentDir(); } else { fprintf(stderr, "%s is not a directory\n", dir.constData()); return Parse_Error; } } if (!dir.endsWith('/')) dir += '/'; const Path file = dir + "compile_commands.json"; if (!file.isFile()) { fprintf(stderr, "no compile_commands.json file in %s\n", dir.constData()); return Parse_Error; } addCompile(dir, Escape_Auto); #endif break; } case HasFileManager: { Path p; if (optarg) { p = optarg; } else if (optind < argc && argv[optind][0] != '-') { p = argv[optind++]; } else { p = "."; } p.resolve(Path::MakeAbsolute); if (!p.exists()) { fprintf(stderr, "%s does not seem to exist\n", optarg); return Parse_Error; } if (p.isDir()) p.append('/'); addQuery(QueryMessage::HasFileManager, p); break; } case ProjectRoot: { Path p = optarg; if (!p.isDir()) { fprintf(stderr, "%s does not seem to be a directory\n", optarg); return Parse_Error; } p.resolve(Path::MakeAbsolute); mProjectRoot = p; break; } case Suspend: { Path p; if (optarg) { p = optarg; } else if (optind < argc && argv[optind][0] != '-') { p = argv[optind++]; } if (!p.isEmpty()) { if (p != "clear" && p != "all") { p.resolve(Path::MakeAbsolute); if (!p.isFile()) { fprintf(stderr, "%s is not a file\n", optarg); return Parse_Error; } } } addQuery(QueryMessage::Suspend, p); break; } case Compile: { String args = optarg; while (optind < argc) { if (!args.isEmpty()) args.append(' '); args.append(argv[optind++]); } if (args == "-" || args.isEmpty()) { char buf[16384]; while (fgets(buf, sizeof(buf), stdin)) { addCompile(Path::pwd(), buf, Escape_Do); } } else { addCompile(Path::pwd(), args, Escape_Dont); } break; } case IsIndexing: addQuery(QueryMessage::IsIndexing); break; case UnescapeCompileCommands: mEscapeMode = Escape_Do; break; case NoUnescapeCompileCommands: mEscapeMode = Escape_Dont; break; case NoSortReferencesByInput: mQueryFlags |= QueryMessage::NoSortReferencesByInput; break; case IsIndexed: case DumpFile: case CheckIncludes: case GenerateTest: case Diagnose: case FixIts: { Path p = optarg; if (!p.exists()) { fprintf(stderr, "%s does not exist\n", optarg); return Parse_Error; } if (!p.isAbsolute()) p.prepend(Path::pwd()); if (p.isDir()) { if (opt->option != IsIndexed) { fprintf(stderr, "%s is not a file\n", optarg); return Parse_Error; } else if (!p.endsWith('/')) { p.append('/'); } } p.resolve(); Flags<QueryMessage::Flag> extraQueryFlags; QueryMessage::Type type = QueryMessage::Invalid; switch (opt->option) { case GenerateTest: type = QueryMessage::GenerateTest; break; case FixIts: type = QueryMessage::FixIts; break; case DumpFile: type = QueryMessage::DumpFile; break; case CheckIncludes: type = QueryMessage::DumpFile; extraQueryFlags |= QueryMessage::DumpCheckIncludes; break; case Diagnose: type = QueryMessage::Diagnose; break; case IsIndexed: type = QueryMessage::IsIndexed; break; default: assert(0); break; } addQuery(type, p, extraQueryFlags); break; } case AllDependencies: { String encoded; List<String> args; while (optind < argc && argv[optind][0] != '-') { args.append(argv[optind++]); } Serializer s(encoded); s << Path() << args; addQuery(QueryMessage::Dependencies, encoded); break; } case DumpFileMaps: case Dependencies: { Path p = optarg; if (!p.isFile()) { fprintf(stderr, "%s is not a file\n", optarg); return Parse_Error; } p.resolve(); List<String> args; while (optind < argc && argv[optind][0] != '-') { args.append(argv[optind++]); } String encoded; Serializer s(encoded); s << p << args; addQuery(opt->option == DumpFileMaps ? QueryMessage::DumpFileMaps : QueryMessage::Dependencies, encoded); break; } case PreprocessFile: { Path p = optarg; p.resolve(Path::MakeAbsolute); if (!p.isFile()) { fprintf(stderr, "%s is not a file\n", optarg); return Parse_Error; } addQuery(QueryMessage::PreprocessFile, p); break; } case RemoveFile: { const Path p = Path::resolved(optarg, Path::MakeAbsolute); if (!p.exists()) { addQuery(QueryMessage::RemoveFile, p); } else { addQuery(QueryMessage::RemoveFile, optarg); } break; } case ReferenceName: addQuery(QueryMessage::ReferencesName, optarg); break; } } if (state == Error) { help(stderr, argv[0]); return Parse_Error; } if (optind < argc) { fprintf(stderr, "rc: unexpected option -- '%s'\n", argv[optind]); return Parse_Error; } if (!initLogging(argv[0], LogStderr, mLogLevel, logFile, logFlags)) { fprintf(stderr, "Can't initialize logging with %d %s %s\n", mLogLevel.toInt(), logFile.constData(), logFlags.toString().constData()); return Parse_Error; } if (mCommands.isEmpty()) { help(stderr, argv[0]); return Parse_Error; } if (mCommands.size() > projectCommands.size()) { // If there's more than one command one likely does not want output from // the queryCommand (unless there's no arg specified for it). This is so // we don't have to pass a different flag for auto-updating project // using the current buffer but rather piggy-back on --project const int count = projectCommands.size(); for (int i=0; i<count; ++i) { std::shared_ptr<QueryCommand> &cmd = projectCommands[i]; if (!cmd->query.isEmpty()) { cmd->extraQueryFlags |= QueryMessage::Silent; } } } if (!logFile.isEmpty() || mLogLevel > LogLevel::Error) { Log l(LogLevel::Warning); l << argc; for (int i = 0; i < argc; ++i) l << " " << argv[i]; } mArgc = argc; mArgv = argv; return Parse_Exec; }