getopts.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1993,97 by Michael Van Canneyt,
  5. member of the Free Pascal development team.
  6. Getopt implementation for Free Pascal, modeled after GNU getopt
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit getopts;
  14. Interface
  15. Const
  16. No_Argument = 0;
  17. Required_Argument = 1;
  18. Optional_Argument = 2;
  19. EndOfOptions = #255;
  20. Type
  21. POption = ^TOption;
  22. TOption = Record
  23. Name : String;
  24. Has_arg : Integer;
  25. Flag : PChar;
  26. Value : Char;
  27. end;
  28. Orderings = (require_order,permute,return_in_order);
  29. Var
  30. OptArg : String;
  31. OptInd : Longint;
  32. OptErr : Boolean;
  33. OptOpt : Char;
  34. Function GetOpt (ShortOpts : String) : char;
  35. Function GetLongOpts (ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  36. Implementation
  37. {$ifdef TP}
  38. uses
  39. strings;
  40. {$endif}
  41. {***************************************************************************
  42. Create an ArgV
  43. ***************************************************************************}
  44. {$ifdef TP}
  45. function GetCommandLine:pchar;
  46. begin
  47. GetCommandLine:=ptr(prefixseg,$81);
  48. end;
  49. function GetCommandFile:pchar;
  50. var
  51. p : pchar;
  52. begin
  53. p:=ptr(memw[prefixseg:$2c],0);
  54. repeat
  55. while p^<>#0 do
  56. inc(longint(p));
  57. { next char also #0 ? }
  58. inc(longint(p));
  59. if p^=#0 then
  60. begin
  61. inc(longint(p),3);
  62. GetCommandFile:=p;
  63. exit;
  64. end;
  65. until false;
  66. end;
  67. type
  68. ppchar = ^pchar;
  69. apchar = array[0..127] of pchar;
  70. var
  71. argc : longint;
  72. argv : apchar;
  73. procedure setup_arguments;
  74. var
  75. arglen,
  76. count : longint;
  77. argstart,
  78. cmdline : pchar;
  79. quote : set of char;
  80. argsbuf : array[0..127] of pchar;
  81. begin
  82. { create argv[0] which is the started filename }
  83. argstart:=GetCommandFile;
  84. arglen:=strlen(argstart)+1;
  85. getmem(argsbuf[0],arglen);
  86. move(argstart^,argsbuf[0]^,arglen);
  87. { create commandline }
  88. cmdline:=GetCommandLine;
  89. count:=1;
  90. repeat
  91. { skip leading spaces }
  92. while cmdline^ in [' ',#9,#13] do
  93. inc(longint(cmdline));
  94. case cmdline^ of
  95. #0 : break;
  96. '"' : begin
  97. quote:=['"'];
  98. inc(longint(cmdline));
  99. end;
  100. '''' : begin
  101. quote:=[''''];
  102. inc(longint(cmdline));
  103. end;
  104. else
  105. quote:=[' ',#9,#13];
  106. end;
  107. { scan until the end of the argument }
  108. argstart:=cmdline;
  109. while (cmdline^<>#0) and not(cmdline^ in quote) do
  110. inc(longint(cmdline));
  111. { reserve some memory }
  112. arglen:=cmdline-argstart;
  113. getmem(argsbuf[count],arglen+1);
  114. move(argstart^,argsbuf[count]^,arglen);
  115. argsbuf[count][arglen]:=#0;
  116. { skip quote }
  117. if cmdline^ in quote then
  118. inc(longint(cmdline));
  119. inc(count);
  120. until false;
  121. { create argc }
  122. argc:=count-1;
  123. { create an nil entry }
  124. argsbuf[count]:=nil;
  125. inc(count);
  126. { create the argv }
  127. { getmem(argv,count shl 2); }
  128. move(argsbuf,argv,count shl 2);
  129. end;
  130. {$endif TP}
  131. {***************************************************************************
  132. Real Getopts
  133. ***************************************************************************}
  134. Var
  135. NextChar,
  136. Nrargs,
  137. first_nonopt,
  138. last_nonopt : Longint;
  139. Ordering : Orderings;
  140. Procedure Exchange;
  141. var
  142. bottom,
  143. middle,
  144. top,i,len : longint;
  145. temp : pchar;
  146. begin
  147. bottom:=first_nonopt;
  148. middle:=last_nonopt;
  149. top:=optind;
  150. while (top>middle) and (middle>bottom) do
  151. begin
  152. if (top-middle>middle-bottom) then
  153. begin
  154. len:=middle-bottom;
  155. for i:=1 to len-1 do
  156. begin
  157. temp:=argv[bottom+i];
  158. argv[bottom+i]:=argv[top-(middle-bottom)+i];
  159. argv[top-(middle-bottom)+i]:=temp;
  160. end;
  161. top:=top-len;
  162. end
  163. else
  164. begin
  165. len:=top-middle;
  166. for i:=0 to len-1 do
  167. begin
  168. temp:=argv[bottom+i];
  169. argv[bottom+i]:=argv[middle+i];
  170. argv[middle+i]:=temp;
  171. end;
  172. bottom:=bottom+len;
  173. end;
  174. end;
  175. first_nonopt:=first_nonopt + optind-last_nonopt;
  176. last_nonopt:=optind;
  177. end; { exchange }
  178. procedure getopt_init (var opts : string);
  179. begin
  180. { Initialize some defaults. }
  181. Optarg:='';
  182. Optind:=1;
  183. First_nonopt:=1;
  184. Last_nonopt:=1;
  185. OptOpt:='?';
  186. Nextchar:=0;
  187. case opts[1] of
  188. '-' : begin
  189. ordering:=return_in_order;
  190. delete(opts,1,1);
  191. end;
  192. '+' : begin
  193. ordering:=require_order;
  194. delete(opts,1,1);
  195. end;
  196. else
  197. ordering:=permute;
  198. end;
  199. end;
  200. Function Internal_getopt (Var Optstring : string;LongOpts : POption;
  201. LongInd : pointer;Long_only : boolean ) : char;
  202. type
  203. pinteger=^integer;
  204. var
  205. temp,endopt,
  206. option_index : byte;
  207. indfound : integer;
  208. currentarg,
  209. optname : string;
  210. p,pfound : POption;
  211. exact,ambig : boolean;
  212. c : char;
  213. begin
  214. optarg:='';
  215. if optind=0 then
  216. getopt_init(optstring);
  217. { Check if We need the next argument. }
  218. if (optind<nrargs) then
  219. currentarg:=strpas(argv[optind])
  220. else
  221. currentarg:='';
  222. if (nextchar=0) then
  223. begin
  224. if ordering=permute then
  225. begin
  226. { If we processed options following non-options : exchange }
  227. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  228. exchange
  229. else
  230. if last_nonopt<>optind then
  231. first_nonopt:=optind;
  232. while (optind<nrargs) and ((argv[optind][0]<>'-') or
  233. (length(strpas(argv[optind]))=1)) do
  234. inc(optind);
  235. last_nonopt:=optind;
  236. end;
  237. { Check for '--' argument }
  238. if optind<nrargs then
  239. currentarg:=strpas(argv[optind])
  240. else
  241. currentarg:='';
  242. if (optind<>nrargs) and (currentarg='--') then
  243. begin
  244. inc(optind);
  245. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  246. exchange
  247. else
  248. if first_nonopt=last_nonopt then
  249. first_nonopt:=optind;
  250. last_nonopt:=nrargs;
  251. optind:=nrargs;
  252. end;
  253. { Are we at the end of all arguments ? }
  254. if optind>=nrargs then
  255. begin
  256. if first_nonopt<>last_nonopt then
  257. optind:=first_nonopt;
  258. Internal_getopt:=EndOfOptions;
  259. exit;
  260. end;
  261. if optind<nrargs then
  262. currentarg:=strpas(argv[optind])
  263. else
  264. currentarg:='';
  265. { Are we at a non-option ? }
  266. if (currentarg[1]<>'-') or (currentarg='-') then
  267. begin
  268. if ordering=require_order then
  269. begin
  270. Internal_getopt:=EndOfOptions;
  271. exit;
  272. end
  273. else
  274. begin
  275. optarg:=strpas(argv[optind]);
  276. inc(optind);
  277. Internal_getopt:=#1;
  278. exit;
  279. end;
  280. end;
  281. { At this point we're at an option ...}
  282. nextchar:=2;
  283. if (longopts<>nil) and (currentarg[2]='-') then
  284. inc(nextchar);
  285. { So, now nextchar points at the first character of an option }
  286. end;
  287. { Check if we have a long option }
  288. if longopts<>nil then
  289. if length(currentarg)>1 then
  290. if (currentarg[2]='-') or
  291. ((not long_only) and (pos(currentarg[2],optstring)<>0)) then
  292. begin
  293. { Get option name }
  294. endopt:=pos('=',currentarg);
  295. if endopt=0 then
  296. endopt:=length(currentarg)+1;
  297. optname:=copy(currentarg,nextchar,endopt-nextchar);
  298. { Match partial or full }
  299. p:=longopts;
  300. pfound:=nil;
  301. exact:=false;
  302. ambig:=false;
  303. option_index:=0;
  304. indfound:=0;
  305. while (p^.name<>'') and (not exact) do
  306. begin
  307. if pos(optname,p^.name)<>0 then
  308. begin
  309. if length(optname)=length(p^.name) then
  310. begin
  311. exact:=true;
  312. pfound:=p;
  313. indfound:=option_index;
  314. end
  315. else
  316. if pfound=nil then
  317. begin
  318. indfound:=option_index;
  319. pfound:=p
  320. end
  321. else
  322. ambig:=true;
  323. end;
  324. inc(longint(p),sizeof(toption));
  325. inc(option_index);
  326. end;
  327. if ambig and not exact then
  328. begin
  329. if opterr then
  330. writeln(argv[0],': option "',optname,'" is ambiguous');
  331. nextchar:=0;
  332. inc(optind);
  333. Internal_getopt:='?';
  334. end;
  335. if pfound<>nil then
  336. begin
  337. inc(optind);
  338. if endopt<=length(currentarg) then
  339. begin
  340. if pfound^.has_arg>0 then
  341. optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt)
  342. else
  343. begin
  344. if opterr then
  345. if currentarg[2]='-' then
  346. writeln(argv[0],': option "--',pfound^.name,'" doesn''t allow an argument')
  347. else
  348. writeln(argv[0],': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument');
  349. nextchar:=0;
  350. internal_getopt:='?';
  351. exit;
  352. end;
  353. end
  354. else { argument in next paramstr... }
  355. begin
  356. if pfound^.has_arg=1 then
  357. begin
  358. if optind<nrargs then
  359. begin
  360. optarg:=strpas(argv[optind]);
  361. inc(optind);
  362. end { required argument }
  363. else
  364. begin { no req argument}
  365. if opterr then
  366. writeln(argv[0],': option ',pfound^.name,' requires an argument');
  367. nextchar:=0;
  368. if optstring[1]=':' then
  369. Internal_getopt:=':'
  370. else
  371. Internal_getopt:='?';
  372. exit;
  373. end;
  374. end;
  375. end; { argument in next parameter end;}
  376. nextchar:=0;
  377. if longind<>nil then
  378. pinteger(longind)^:=indfound+1;
  379. if pfound^.flag<>nil then
  380. begin
  381. pfound^.flag^:=pfound^.value;
  382. internal_getopt:=#0;
  383. exit;
  384. end;
  385. internal_getopt:=pfound^.value;
  386. exit;
  387. end; { pfound<>nil }
  388. { We didn't find it as an option }
  389. if (not long_only) or
  390. ((currentarg[2]='-') or (pos(CurrentArg[nextchar],optstring)=0)) then
  391. begin
  392. if opterr then
  393. if currentarg[2]='-' then
  394. writeln(argv[0],' unrecognized option "--',optname,'"')
  395. else
  396. writeln(argv[0],' unrecognized option "',currentarg[1],optname,'"');
  397. nextchar:=0;
  398. inc(optind);
  399. Internal_getopt:='?';
  400. exit;
  401. end;
  402. end; { Of long options.}
  403. { We check for a short option. }
  404. temp:=pos(currentarg[nextchar],optstring);
  405. c:=currentarg[nextchar];
  406. inc(nextchar);
  407. if nextchar>length(currentarg) then
  408. begin
  409. inc(optind);
  410. nextchar:=0;
  411. end;
  412. if (temp=0) or (c=':') then
  413. begin
  414. if opterr then
  415. writeln(argv[0],': illegal option -- ',c);
  416. optopt:=c;
  417. internal_getopt:='?';
  418. exit;
  419. end;
  420. Internal_getopt:=optstring[temp];
  421. if optstring[temp+1]=':' then
  422. if currentarg[temp+2]=':' then
  423. begin { optional argument }
  424. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  425. nextchar:=0;
  426. end
  427. else
  428. begin { required argument }
  429. if nextchar>0 then
  430. begin
  431. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  432. inc(optind);
  433. end
  434. else
  435. if (optind=nrargs) then
  436. begin
  437. if opterr then
  438. writeln (argv[0],': option requires an argument -- ',optstring[temp]);
  439. optopt:=optstring[temp];
  440. if optstring[1]=':' then
  441. Internal_getopt:=':'
  442. else
  443. Internal_Getopt:='?';
  444. end
  445. else
  446. begin
  447. optarg:=strpas(argv[optind]);
  448. inc(optind)
  449. end;
  450. nextchar:=0;
  451. end; { End of required argument}
  452. end; { End of internal getopt...}
  453. Function GetOpt(ShortOpts : String) : char;
  454. begin
  455. getopt:=internal_getopt(shortopts,nil,nil,false);
  456. end;
  457. Function GetLongOpts(ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  458. begin
  459. getlongopts:=internal_getopt(shortopts,longopts,@longind,true);
  460. end;
  461. begin
  462. { create argv if running under TP }
  463. {$ifdef TP}
  464. setup_arguments;
  465. {$endif}
  466. { Needed to detect startup }
  467. Opterr:=true;
  468. Optind:=0;
  469. nrargs:=argc;
  470. end.
  471. {
  472. $Log$
  473. Revision 1.3 1998-06-18 10:49:04 peter
  474. * some fixes with indexes
  475. * bp7 compatible
  476. Revision 1.2 1998/05/21 19:30:57 peter
  477. * objects compiles for linux
  478. + assign(pchar), assign(char), rename(pchar), rename(char)
  479. * fixed read_text_as_array
  480. + read_text_as_pchar which was not yet in the rtl
  481. Revision 1.1 1998/05/12 10:42:45 peter
  482. * moved getopts to inc/, all supported OS's need argc,argv exported
  483. + strpas, strlen are now exported in the systemunit
  484. * removed logs
  485. * removed $ifdef ver_above
  486. }