ParseFTPList.cpp 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "ParseFTPList.h"
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <ctype.h>
  9. #include "plstr.h"
  10. #include "nsDebug.h"
  11. #include "prprf.h"
  12. #include "mozilla/IntegerPrintfMacros.h"
  13. #include "mozilla/Sprintf.h"
  14. /* ==================================================================== */
  15. static inline int ParsingFailed(struct list_state *state)
  16. {
  17. if (state->parsed_one || state->lstyle) /* junk if we fail to parse */
  18. return '?'; /* this time but had previously parsed successfully */
  19. return '"'; /* its part of a comment or error message */
  20. }
  21. int ParseFTPList(const char *line, struct list_state *state,
  22. struct list_result *result )
  23. {
  24. unsigned int carry_buf_len; /* copy of state->carry_buf_len */
  25. unsigned int pos;
  26. const char *p;
  27. if (!line || !state || !result)
  28. return 0;
  29. memset( result, 0, sizeof(*result) );
  30. state->numlines++;
  31. /* carry buffer is only valid from one line to the next */
  32. carry_buf_len = state->carry_buf_len;
  33. state->carry_buf_len = 0;
  34. /* strip leading whitespace */
  35. while (*line == ' ' || *line == '\t')
  36. line++;
  37. /* line is terminated at first '\0' or '\n' */
  38. p = line;
  39. while (*p && *p != '\n')
  40. p++;
  41. unsigned int linelen = p - line;
  42. if (linelen > 0 && *p == '\n' && *(p-1) == '\r')
  43. linelen--;
  44. /* DON'T strip trailing whitespace. */
  45. if (linelen > 0)
  46. {
  47. static const char *month_names = "JanFebMarAprMayJunJulAugSepOctNovDec";
  48. const char *tokens[16]; /* 16 is more than enough */
  49. unsigned int toklen[(sizeof(tokens)/sizeof(tokens[0]))];
  50. unsigned int linelen_sans_wsp; // line length sans whitespace
  51. unsigned int numtoks = 0;
  52. unsigned int tokmarker = 0; /* extra info for lstyle handler */
  53. unsigned int month_num = 0;
  54. char tbuf[4];
  55. int lstyle = 0;
  56. if (carry_buf_len) /* VMS long filename carryover buffer */
  57. {
  58. tokens[0] = state->carry_buf;
  59. toklen[0] = carry_buf_len;
  60. numtoks++;
  61. }
  62. pos = 0;
  63. while (pos < linelen && numtoks < (sizeof(tokens)/sizeof(tokens[0])) )
  64. {
  65. while (pos < linelen &&
  66. (line[pos] == ' ' || line[pos] == '\t' || line[pos] == '\r'))
  67. pos++;
  68. if (pos < linelen)
  69. {
  70. tokens[numtoks] = &line[pos];
  71. while (pos < linelen &&
  72. (line[pos] != ' ' && line[pos] != '\t' && line[pos] != '\r'))
  73. pos++;
  74. if (tokens[numtoks] != &line[pos])
  75. {
  76. toklen[numtoks] = (&line[pos] - tokens[numtoks]);
  77. numtoks++;
  78. }
  79. }
  80. }
  81. if (!numtoks)
  82. return ParsingFailed(state);
  83. linelen_sans_wsp = &(tokens[numtoks-1][toklen[numtoks-1]]) - tokens[0];
  84. if (numtoks == (sizeof(tokens)/sizeof(tokens[0])) )
  85. {
  86. pos = linelen;
  87. while (pos > 0 && (line[pos-1] == ' ' || line[pos-1] == '\t'))
  88. pos--;
  89. linelen_sans_wsp = pos;
  90. }
  91. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  92. #if defined(SUPPORT_EPLF)
  93. /* EPLF handling must come somewhere before /bin/dls handling. */
  94. if (!lstyle && (!state->lstyle || state->lstyle == 'E'))
  95. {
  96. if (*line == '+' && linelen > 4 && numtoks >= 2)
  97. {
  98. pos = 1;
  99. while (pos < (linelen-1))
  100. {
  101. p = &line[pos++];
  102. if (*p == '/')
  103. result->fe_type = 'd'; /* its a dir */
  104. else if (*p == 'r')
  105. result->fe_type = 'f'; /* its a file */
  106. else if (*p == 'm')
  107. {
  108. if (isdigit(line[pos]))
  109. {
  110. while (pos < linelen && isdigit(line[pos]))
  111. pos++;
  112. if (pos < linelen && line[pos] == ',')
  113. {
  114. PRTime t;
  115. PRTime seconds;
  116. PR_sscanf(p+1, "%llu", &seconds);
  117. t = seconds * PR_USEC_PER_SEC;
  118. PR_ExplodeTime(t, PR_LocalTimeParameters, &(result->fe_time) );
  119. }
  120. }
  121. }
  122. else if (*p == 's')
  123. {
  124. if (isdigit(line[pos]))
  125. {
  126. while (pos < linelen && isdigit(line[pos]))
  127. pos++;
  128. if (pos < linelen && line[pos] == ',' &&
  129. ((&line[pos]) - (p+1)) < int(sizeof(result->fe_size)-1) )
  130. {
  131. memcpy( result->fe_size, p+1, (unsigned)(&line[pos] - (p+1)) );
  132. result->fe_size[(&line[pos] - (p+1))] = '\0';
  133. }
  134. }
  135. }
  136. else if (isalpha(*p)) /* 'i'/'up' or unknown "fact" (property) */
  137. {
  138. while (pos < linelen && *++p != ',')
  139. pos++;
  140. }
  141. else if (*p != '\t' || (p+1) != tokens[1])
  142. {
  143. break; /* its not EPLF after all */
  144. }
  145. else
  146. {
  147. state->parsed_one = 1;
  148. state->lstyle = lstyle = 'E';
  149. p = &(line[linelen_sans_wsp]);
  150. result->fe_fname = tokens[1];
  151. result->fe_fnlen = p - tokens[1];
  152. if (!result->fe_type) /* access denied */
  153. {
  154. result->fe_type = 'f'; /* is assuming 'f'ile correct? */
  155. return '?'; /* NO! junk it. */
  156. }
  157. return result->fe_type;
  158. }
  159. if (pos >= (linelen-1) || line[pos] != ',')
  160. break;
  161. pos++;
  162. } /* while (pos < linelen) */
  163. memset( result, 0, sizeof(*result) );
  164. } /* if (*line == '+' && linelen > 4 && numtoks >= 2) */
  165. } /* if (!lstyle && (!state->lstyle || state->lstyle == 'E')) */
  166. #endif /* SUPPORT_EPLF */
  167. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  168. #if defined(SUPPORT_VMS)
  169. if (!lstyle && (!state->lstyle || state->lstyle == 'V'))
  170. { /* try VMS Multinet/UCX/CMS server */
  171. /*
  172. * Legal characters in a VMS file/dir spec are [A-Z0-9$.-_~].
  173. * '$' cannot begin a filename and `-' cannot be used as the first
  174. * or last character. '.' is only valid as a directory separator
  175. * and <file>.<type> separator. A canonical filename spec might look
  176. * like this: DISK$VOL:[DIR1.DIR2.DIR3]FILE.TYPE;123
  177. * All VMS FTP servers LIST in uppercase.
  178. *
  179. * We need to be picky about this in order to support
  180. * multi-line listings correctly.
  181. */
  182. if (!state->parsed_one &&
  183. (numtoks == 1 || (numtoks == 2 && toklen[0] == 9 &&
  184. memcmp(tokens[0], "Directory", 9)==0 )))
  185. {
  186. /* If no dirstyle has been detected yet, and this line is a
  187. * VMS list's dirname, then turn on VMS dirstyle.
  188. * eg "ACA:[ANONYMOUS]", "DISK$FTP:[ANONYMOUS]", "SYS$ANONFTP:"
  189. */
  190. p = tokens[0];
  191. pos = toklen[0];
  192. if (numtoks == 2)
  193. {
  194. p = tokens[1];
  195. pos = toklen[1];
  196. }
  197. pos--;
  198. if (pos >= 3)
  199. {
  200. while (pos > 0 && p[pos] != '[')
  201. {
  202. pos--;
  203. if (p[pos] == '-' || p[pos] == '$')
  204. {
  205. if (pos == 0 || p[pos-1] == '[' || p[pos-1] == '.' ||
  206. (p[pos] == '-' && (p[pos+1] == ']' || p[pos+1] == '.')))
  207. break;
  208. }
  209. else if (p[pos] != '.' && p[pos] != '~' &&
  210. !isdigit(p[pos]) && !isalpha(p[pos]))
  211. break;
  212. else if (isalpha(p[pos]) && p[pos] != toupper(p[pos]))
  213. break;
  214. }
  215. if (pos > 0)
  216. {
  217. pos--;
  218. if (p[pos] != ':' || p[pos+1] != '[')
  219. pos = 0;
  220. }
  221. }
  222. if (pos > 0 && p[pos] == ':')
  223. {
  224. while (pos > 0)
  225. {
  226. pos--;
  227. if (p[pos] != '$' && p[pos] != '_' && p[pos] != '-' &&
  228. p[pos] != '~' && !isdigit(p[pos]) && !isalpha(p[pos]))
  229. break;
  230. else if (isalpha(p[pos]) && p[pos] != toupper(p[pos]))
  231. break;
  232. }
  233. if (pos == 0)
  234. {
  235. state->lstyle = 'V';
  236. return '?'; /* its junk */
  237. }
  238. }
  239. /* fallthrough */
  240. }
  241. else if ((tokens[0][toklen[0]-1]) != ';')
  242. {
  243. if (numtoks == 1 && (state->lstyle == 'V' && !carry_buf_len))
  244. lstyle = 'V';
  245. else if (numtoks < 4)
  246. ;
  247. else if (toklen[1] >= 10 && memcmp(tokens[1], "%RMS-E-PRV", 10) == 0)
  248. lstyle = 'V';
  249. else if ((&line[linelen] - tokens[1]) >= 22 &&
  250. memcmp(tokens[1], "insufficient privilege", 22) == 0)
  251. lstyle = 'V';
  252. else if (numtoks != 4 && numtoks != 6)
  253. ;
  254. else if (numtoks == 6 && (
  255. toklen[5] < 4 || *tokens[5] != '(' || /* perms */
  256. (tokens[5][toklen[5]-1]) != ')' ))
  257. ;
  258. else if ( (toklen[2] == 10 || toklen[2] == 11) &&
  259. (tokens[2][toklen[2]-5]) == '-' &&
  260. (tokens[2][toklen[2]-9]) == '-' &&
  261. (((toklen[3]==4 || toklen[3]==5 || toklen[3]==7 || toklen[3]==8) &&
  262. (tokens[3][toklen[3]-3]) == ':' ) ||
  263. ((toklen[3]==10 || toklen[3]==11 ) &&
  264. (tokens[3][toklen[3]-3]) == '.' )
  265. ) && /* time in [H]H:MM[:SS[.CC]] format */
  266. isdigit(*tokens[1]) && /* size */
  267. isdigit(*tokens[2]) && /* date */
  268. isdigit(*tokens[3]) /* time */
  269. )
  270. {
  271. lstyle = 'V';
  272. }
  273. if (lstyle == 'V')
  274. {
  275. /*
  276. * MultiNet FTP:
  277. * LOGIN.COM;2 1 4-NOV-1994 04:09 [ANONYMOUS] (RWE,RWE,,)
  278. * PUB.DIR;1 1 27-JAN-1994 14:46 [ANONYMOUS] (RWE,RWE,RE,RWE)
  279. * README.FTP;1 %RMS-E-PRV, insufficient privilege or file protection violation
  280. * ROUSSOS.DIR;1 1 27-JAN-1994 14:48 [CS,ROUSSOS] (RWE,RWE,RE,R)
  281. * S67-50903.JPG;1 328 22-SEP-1998 16:19 [ANONYMOUS] (RWED,RWED,,)
  282. * UCX FTP:
  283. * CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)
  284. * CMU/VMS-IP FTP
  285. * [VMSSERV.FILES]ALARM.DIR;1 1/3 5-MAR-1993 18:09
  286. * TCPware FTP
  287. * FOO.BAR;1 4 5-MAR-1993 18:09:01.12
  288. * Long filename example:
  289. * THIS-IS-A-LONG-VMS-FILENAME.AND-THIS-IS-A-LONG-VMS-FILETYPE\r\n
  290. * 213[/nnn] 29-JAN-1996 03:33[:nn] [ANONYMOU,ANONYMOUS] (RWED,RWED,,)
  291. */
  292. tokmarker = 0;
  293. p = tokens[0];
  294. pos = 0;
  295. if (*p == '[' && toklen[0] >= 4) /* CMU style */
  296. {
  297. if (p[1] != ']')
  298. {
  299. p++;
  300. pos++;
  301. }
  302. while (lstyle && pos < toklen[0] && *p != ']')
  303. {
  304. if (*p != '$' && *p != '.' && *p != '_' && *p != '-' &&
  305. *p != '~' && !isdigit(*p) && !isalpha(*p))
  306. lstyle = 0;
  307. pos++;
  308. p++;
  309. }
  310. if (lstyle && pos < (toklen[0]-1))
  311. {
  312. /* ']' was found and there is at least one character after it */
  313. NS_ASSERTION(*p == ']', "unexpected state");
  314. pos++;
  315. p++;
  316. tokmarker = pos; /* length of leading "[DIR1.DIR2.etc]" */
  317. } else {
  318. /* not a CMU style listing */
  319. lstyle = 0;
  320. }
  321. }
  322. while (lstyle && pos < toklen[0] && *p != ';')
  323. {
  324. if (*p != '$' && *p != '.' && *p != '_' && *p != '-' &&
  325. *p != '~' && !isdigit(*p) && !isalpha(*p))
  326. lstyle = 0;
  327. else if (isalpha(*p) && *p != toupper(*p))
  328. lstyle = 0;
  329. p++;
  330. pos++;
  331. }
  332. if (lstyle && *p == ';')
  333. {
  334. if (pos == 0 || pos == (toklen[0]-1))
  335. lstyle = 0;
  336. for (pos++;lstyle && pos < toklen[0];pos++)
  337. {
  338. if (!isdigit(tokens[0][pos]))
  339. lstyle = 0;
  340. }
  341. }
  342. pos = (p - tokens[0]); /* => fnlength sans ";####" */
  343. pos -= tokmarker; /* => fnlength sans "[DIR1.DIR2.etc]" */
  344. p = &(tokens[0][tokmarker]); /* offset of basename */
  345. if (!lstyle || pos == 0 || pos > 80) /* VMS filenames can't be longer than that */
  346. {
  347. lstyle = 0;
  348. }
  349. else if (numtoks == 1)
  350. {
  351. /* if VMS has been detected and there is only one token and that
  352. * token was a VMS filename then this is a multiline VMS LIST entry.
  353. */
  354. if (pos >= (sizeof(state->carry_buf)-1))
  355. pos = (sizeof(state->carry_buf)-1); /* shouldn't happen */
  356. memcpy( state->carry_buf, p, pos );
  357. state->carry_buf_len = pos;
  358. return '?'; /* tell caller to treat as junk */
  359. }
  360. else if (isdigit(*tokens[1])) /* not no-privs message */
  361. {
  362. for (pos = 0; lstyle && pos < (toklen[1]); pos++)
  363. {
  364. if (!isdigit((tokens[1][pos])) && (tokens[1][pos]) != '/')
  365. lstyle = 0;
  366. }
  367. if (lstyle && numtoks > 4) /* Multinet or UCX but not CMU */
  368. {
  369. for (pos = 1; lstyle && pos < (toklen[5]-1); pos++)
  370. {
  371. p = &(tokens[5][pos]);
  372. if (*p!='R' && *p!='W' && *p!='E' && *p!='D' && *p!=',')
  373. lstyle = 0;
  374. }
  375. }
  376. }
  377. } /* passed initial tests */
  378. } /* else if ((tokens[0][toklen[0]-1]) != ';') */
  379. if (lstyle == 'V')
  380. {
  381. state->parsed_one = 1;
  382. state->lstyle = lstyle;
  383. if (isdigit(*tokens[1])) /* not permission denied etc */
  384. {
  385. /* strip leading directory name */
  386. if (*tokens[0] == '[') /* CMU server */
  387. {
  388. pos = toklen[0]-1;
  389. p = tokens[0]+1;
  390. while (*p != ']')
  391. {
  392. p++;
  393. pos--;
  394. }
  395. toklen[0] = --pos;
  396. tokens[0] = ++p;
  397. }
  398. pos = 0;
  399. while (pos < toklen[0] && (tokens[0][pos]) != ';')
  400. pos++;
  401. result->fe_cinfs = 1;
  402. result->fe_type = 'f';
  403. result->fe_fname = tokens[0];
  404. result->fe_fnlen = pos;
  405. if (pos > 4)
  406. {
  407. p = &(tokens[0][pos-4]);
  408. if (p[0] == '.' && p[1] == 'D' && p[2] == 'I' && p[3] == 'R')
  409. {
  410. result->fe_fnlen -= 4;
  411. result->fe_type = 'd';
  412. }
  413. }
  414. if (result->fe_type != 'd')
  415. {
  416. /* #### or used/allocated form. If used/allocated form, then
  417. * 'used' is the size in bytes if and only if 'used'<=allocated.
  418. * If 'used' is size in bytes then it can be > 2^32
  419. * If 'used' is not size in bytes then it is size in blocks.
  420. */
  421. pos = 0;
  422. while (pos < toklen[1] && (tokens[1][pos]) != '/')
  423. pos++;
  424. /*
  425. * I've never seen size come back in bytes, its always in blocks, and
  426. * the following test fails. So, always perform the "size in blocks".
  427. * I'm leaving the "size in bytes" code if'd out in case we ever need
  428. * to re-instate it.
  429. */
  430. #if 0
  431. if (pos < toklen[1] && ( (pos<<1) > (toklen[1]-1) ||
  432. (strtoul(tokens[1], (char **)0, 10) >
  433. strtoul(tokens[1]+pos+1, (char **)0, 10)) ))
  434. { /* size is in bytes */
  435. if (pos > (sizeof(result->fe_size)-1))
  436. pos = sizeof(result->fe_size)-1;
  437. memcpy( result->fe_size, tokens[1], pos );
  438. result->fe_size[pos] = '\0';
  439. }
  440. else /* size is in blocks */
  441. #endif
  442. {
  443. /* size requires multiplication by blocksize.
  444. *
  445. * We could assume blocksize is 512 (like Lynx does) and
  446. * shift by 9, but that might not be right. Even if it
  447. * were, doing that wouldn't reflect what the file's
  448. * real size was. The sanest thing to do is not use the
  449. * LISTing's filesize, so we won't (like ftpmirror).
  450. *
  451. * ulltoa(((unsigned long long)fsz)<<9, result->fe_size, 10);
  452. *
  453. * A block is always 512 bytes on OpenVMS, compute size.
  454. * So its rounded up to the next block, so what, its better
  455. * than not showing the size at all.
  456. * A block is always 512 bytes on OpenVMS, compute size.
  457. * So its rounded up to the next block, so what, its better
  458. * than not showing the size at all.
  459. */
  460. uint64_t fsz = uint64_t(strtoul(tokens[1], (char **)0, 10) * 512);
  461. SprintfLiteral(result->fe_size, "%" PRId64, fsz);
  462. }
  463. } /* if (result->fe_type != 'd') */
  464. p = tokens[2] + 2;
  465. if (*p == '-')
  466. p++;
  467. tbuf[0] = p[0];
  468. tbuf[1] = tolower(p[1]);
  469. tbuf[2] = tolower(p[2]);
  470. month_num = 0;
  471. for (pos = 0; pos < (12*3); pos+=3)
  472. {
  473. if (tbuf[0] == month_names[pos+0] &&
  474. tbuf[1] == month_names[pos+1] &&
  475. tbuf[2] == month_names[pos+2])
  476. break;
  477. month_num++;
  478. }
  479. if (month_num >= 12)
  480. month_num = 0;
  481. result->fe_time.tm_month = month_num;
  482. result->fe_time.tm_mday = atoi(tokens[2]);
  483. result->fe_time.tm_year = atoi(p+4); // NSPR wants year as XXXX
  484. p = tokens[3] + 2;
  485. if (*p == ':')
  486. p++;
  487. if (p[2] == ':')
  488. result->fe_time.tm_sec = atoi(p+3);
  489. result->fe_time.tm_hour = atoi(tokens[3]);
  490. result->fe_time.tm_min = atoi(p);
  491. return result->fe_type;
  492. } /* if (isdigit(*tokens[1])) */
  493. return '?'; /* junk */
  494. } /* if (lstyle == 'V') */
  495. } /* if (!lstyle && (!state->lstyle || state->lstyle == 'V')) */
  496. #endif
  497. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  498. #if defined(SUPPORT_CMS)
  499. /* Virtual Machine/Conversational Monitor System (IBM Mainframe) */
  500. if (!lstyle && (!state->lstyle || state->lstyle == 'C')) /* VM/CMS */
  501. {
  502. /* LISTing according to mirror.pl
  503. * Filename FileType Fm Format Lrecl Records Blocks Date Time
  504. * LASTING GLOBALV A1 V 41 21 1 9/16/91 15:10:32
  505. * J43401 NETLOG A0 V 77 1 1 9/12/91 12:36:04
  506. * PROFILE EXEC A1 V 17 3 1 9/12/91 12:39:07
  507. * DIRUNIX SCRIPT A1 V 77 1216 17 1/04/93 20:30:47
  508. * MAIL PROFILE A2 F 80 1 1 10/14/92 16:12:27
  509. * BADY2K TEXT A0 V 1 1 1 1/03/102 10:11:12
  510. * AUTHORS A1 DIR - - - 9/20/99 10:31:11
  511. *
  512. * LISTing from vm.marist.edu and vm.sc.edu
  513. * 220-FTPSERVE IBM VM Level 420 at VM.MARIST.EDU, 04:58:12 EDT WEDNESDAY 2002-07-10
  514. * AUTHORS DIR - - - 1999-09-20 10:31:11 -
  515. * HARRINGTON DIR - - - 1997-02-12 15:33:28 -
  516. * PICS DIR - - - 2000-10-12 15:43:23 -
  517. * SYSFILE DIR - - - 2000-07-20 17:48:01 -
  518. * WELCNVT EXEC V 72 9 1 1999-09-20 17:16:18 -
  519. * WELCOME EREADME F 80 21 1 1999-12-27 16:19:00 -
  520. * WELCOME README V 82 21 1 1999-12-27 16:19:04 -
  521. * README ANONYMOU V 71 26 1 1997-04-02 12:33:20 TCP291
  522. * README ANONYOLD V 71 15 1 1995-08-25 16:04:27 TCP291
  523. */
  524. if (numtoks >= 7 && (toklen[0]+toklen[1]) <= 16)
  525. {
  526. for (pos = 1; !lstyle && (pos+5) < numtoks; pos++)
  527. {
  528. p = tokens[pos];
  529. if ((toklen[pos] == 1 && (*p == 'F' || *p == 'V')) ||
  530. (toklen[pos] == 3 && *p == 'D' && p[1] == 'I' && p[2] == 'R'))
  531. {
  532. if (toklen[pos+5] == 8 && (tokens[pos+5][2]) == ':' &&
  533. (tokens[pos+5][5]) == ':' )
  534. {
  535. p = tokens[pos+4];
  536. if ((toklen[pos+4] == 10 && p[4] == '-' && p[7] == '-') ||
  537. (toklen[pos+4] >= 7 && toklen[pos+4] <= 9 &&
  538. p[((p[1]!='/')?(2):(1))] == '/' &&
  539. p[((p[1]!='/')?(5):(4))] == '/'))
  540. /* Y2K bugs possible ("7/06/102" or "13/02/101") */
  541. {
  542. if ( (*tokens[pos+1] == '-' &&
  543. *tokens[pos+2] == '-' &&
  544. *tokens[pos+3] == '-') ||
  545. (isdigit(*tokens[pos+1]) &&
  546. isdigit(*tokens[pos+2]) &&
  547. isdigit(*tokens[pos+3])) )
  548. {
  549. lstyle = 'C';
  550. tokmarker = pos;
  551. }
  552. }
  553. }
  554. }
  555. } /* for (pos = 1; !lstyle && (pos+5) < numtoks; pos++) */
  556. } /* if (numtoks >= 7) */
  557. /* extra checking if first pass */
  558. if (lstyle && !state->lstyle)
  559. {
  560. for (pos = 0, p = tokens[0]; lstyle && pos < toklen[0]; pos++, p++)
  561. {
  562. if (isalpha(*p) && toupper(*p) != *p)
  563. lstyle = 0;
  564. }
  565. for (pos = tokmarker+1; pos <= tokmarker+3; pos++)
  566. {
  567. if (!(toklen[pos] == 1 && *tokens[pos] == '-'))
  568. {
  569. for (p = tokens[pos]; lstyle && p<(tokens[pos]+toklen[pos]); p++)
  570. {
  571. if (!isdigit(*p))
  572. lstyle = 0;
  573. }
  574. }
  575. }
  576. for (pos = 0, p = tokens[tokmarker+4];
  577. lstyle && pos < toklen[tokmarker+4]; pos++, p++)
  578. {
  579. if (*p == '/')
  580. {
  581. /* There may be Y2K bugs in the date. Don't simplify to
  582. * pos != (len-3) && pos != (len-6) like time is done.
  583. */
  584. if ((tokens[tokmarker+4][1]) == '/')
  585. {
  586. if (pos != 1 && pos != 4)
  587. lstyle = 0;
  588. }
  589. else if (pos != 2 && pos != 5)
  590. lstyle = 0;
  591. }
  592. else if (*p != '-' && !isdigit(*p))
  593. lstyle = 0;
  594. else if (*p == '-' && pos != 4 && pos != 7)
  595. lstyle = 0;
  596. }
  597. for (pos = 0, p = tokens[tokmarker+5];
  598. lstyle && pos < toklen[tokmarker+5]; pos++, p++)
  599. {
  600. if (*p != ':' && !isdigit(*p))
  601. lstyle = 0;
  602. else if (*p == ':' && pos != (toklen[tokmarker+5]-3)
  603. && pos != (toklen[tokmarker+5]-6))
  604. lstyle = 0;
  605. }
  606. } /* initial if() */
  607. if (lstyle == 'C')
  608. {
  609. state->parsed_one = 1;
  610. state->lstyle = lstyle;
  611. p = tokens[tokmarker+4];
  612. if (toklen[tokmarker+4] == 10) /* newstyle: YYYY-MM-DD format */
  613. {
  614. result->fe_time.tm_year = atoi(p+0) - 1900;
  615. result->fe_time.tm_month = atoi(p+5) - 1;
  616. result->fe_time.tm_mday = atoi(p+8);
  617. }
  618. else /* oldstyle: [M]M/DD/YY format */
  619. {
  620. pos = toklen[tokmarker+4];
  621. result->fe_time.tm_month = atoi(p) - 1;
  622. result->fe_time.tm_mday = atoi((p+pos)-5);
  623. result->fe_time.tm_year = atoi((p+pos)-2);
  624. if (result->fe_time.tm_year < 70)
  625. result->fe_time.tm_year += 100;
  626. }
  627. p = tokens[tokmarker+5];
  628. pos = toklen[tokmarker+5];
  629. result->fe_time.tm_hour = atoi(p);
  630. result->fe_time.tm_min = atoi((p+pos)-5);
  631. result->fe_time.tm_sec = atoi((p+pos)-2);
  632. result->fe_cinfs = 1;
  633. result->fe_fname = tokens[0];
  634. result->fe_fnlen = toklen[0];
  635. result->fe_type = 'f';
  636. p = tokens[tokmarker];
  637. if (toklen[tokmarker] == 3 && *p=='D' && p[1]=='I' && p[2]=='R')
  638. result->fe_type = 'd';
  639. if ((/*newstyle*/ toklen[tokmarker+4] == 10 && tokmarker > 1) ||
  640. (/*oldstyle*/ toklen[tokmarker+4] != 10 && tokmarker > 2))
  641. { /* have a filetype column */
  642. char *dot;
  643. p = &(tokens[0][toklen[0]]);
  644. memcpy( &dot, &p, sizeof(dot) ); /* NASTY! */
  645. *dot++ = '.';
  646. p = tokens[1];
  647. for (pos = 0; pos < toklen[1]; pos++)
  648. *dot++ = *p++;
  649. result->fe_fnlen += 1 + toklen[1];
  650. }
  651. /* oldstyle LISTING:
  652. * files/dirs not on the 'A' minidisk are not RETRievable/CHDIRable
  653. if (toklen[tokmarker+4] != 10 && *tokens[tokmarker-1] != 'A')
  654. return '?';
  655. */
  656. /* VM/CMS LISTings have no usable filesize field.
  657. * Have to use the 'SIZE' command for that.
  658. */
  659. return result->fe_type;
  660. } /* if (lstyle == 'C' && (!state->lstyle || state->lstyle == lstyle)) */
  661. } /* VM/CMS */
  662. #endif
  663. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  664. #if defined(SUPPORT_DOS) /* WinNT DOS dirstyle */
  665. if (!lstyle && (!state->lstyle || state->lstyle == 'W'))
  666. {
  667. /*
  668. * "10-23-00 01:27PM <DIR> veronist"
  669. * "06-15-00 07:37AM <DIR> zoe"
  670. * "07-14-00 01:35PM 2094926 canprankdesk.tif"
  671. * "07-21-00 01:19PM 95077 Jon Kauffman Enjoys the Good Life.jpg"
  672. * "07-21-00 01:19PM 52275 Name Plate.jpg"
  673. * "07-14-00 01:38PM 2250540 Valentineoffprank-HiRes.jpg"
  674. */
  675. // Microsoft FTP server with FtpDirBrowseShowLongDate set returns year
  676. // in 4-digit format:
  677. // "10-10-2014 10:10AM <DIR> FTP"
  678. // Windows CE FTP server returns time in 24-hour format:
  679. // "05-03-13 22:01 <DIR> APPS"
  680. if ((numtoks >= 4) && (toklen[0] == 8 || toklen[0] == 10) &&
  681. (toklen[1] == 5 || toklen[1] == 7) &&
  682. (*tokens[2] == '<' || isdigit(*tokens[2])) )
  683. {
  684. p = tokens[0];
  685. if ( isdigit(p[0]) && isdigit(p[1]) && p[2]=='-' &&
  686. isdigit(p[3]) && isdigit(p[4]) && p[5]=='-' &&
  687. isdigit(p[6]) && isdigit(p[7]) )
  688. {
  689. p = tokens[1];
  690. if ( isdigit(p[0]) && isdigit(p[1]) && p[2]==':' &&
  691. isdigit(p[3]) && isdigit(p[4]) &&
  692. (toklen[1] == 5 || (toklen[1] == 7 &&
  693. (p[5]=='A' || p[5]=='P') && p[6]=='M')))
  694. {
  695. lstyle = 'W';
  696. if (!state->lstyle)
  697. {
  698. p = tokens[2];
  699. /* <DIR> or <JUNCTION> */
  700. if (*p != '<' || p[toklen[2]-1] != '>')
  701. {
  702. for (pos = 1; (lstyle && pos < toklen[2]); pos++)
  703. {
  704. if (!isdigit(*++p))
  705. lstyle = 0;
  706. }
  707. }
  708. }
  709. }
  710. }
  711. }
  712. if (lstyle == 'W')
  713. {
  714. state->parsed_one = 1;
  715. state->lstyle = lstyle;
  716. p = &(line[linelen]); /* line end */
  717. result->fe_cinfs = 1;
  718. result->fe_fname = tokens[3];
  719. result->fe_fnlen = p - tokens[3];
  720. result->fe_type = 'd';
  721. if (*tokens[2] != '<') /* not <DIR> or <JUNCTION> */
  722. {
  723. // try to handle correctly spaces at the beginning of the filename
  724. // filesize (token[2]) must end at offset 38
  725. if (tokens[2] + toklen[2] - line == 38) {
  726. result->fe_fname = &(line[39]);
  727. result->fe_fnlen = p - result->fe_fname;
  728. }
  729. result->fe_type = 'f';
  730. pos = toklen[2];
  731. while (pos > (sizeof(result->fe_size)-1))
  732. pos = (sizeof(result->fe_size)-1);
  733. memcpy( result->fe_size, tokens[2], pos );
  734. result->fe_size[pos] = '\0';
  735. }
  736. else {
  737. // try to handle correctly spaces at the beginning of the filename
  738. // token[2] must begin at offset 24, the length is 5 or 10
  739. // token[3] must begin at offset 39 or higher
  740. if (tokens[2] - line == 24 && (toklen[2] == 5 || toklen[2] == 10) &&
  741. tokens[3] - line >= 39) {
  742. result->fe_fname = &(line[39]);
  743. result->fe_fnlen = p - result->fe_fname;
  744. }
  745. if ((tokens[2][1]) != 'D') /* not <DIR> */
  746. {
  747. result->fe_type = '?'; /* unknown until junc for sure */
  748. if (result->fe_fnlen > 4)
  749. {
  750. p = result->fe_fname;
  751. for (pos = result->fe_fnlen - 4; pos > 0; pos--)
  752. {
  753. if (p[0] == ' ' && p[3] == ' ' && p[2] == '>' &&
  754. (p[1] == '=' || p[1] == '-'))
  755. {
  756. result->fe_type = 'l';
  757. result->fe_fnlen = p - result->fe_fname;
  758. result->fe_lname = p + 4;
  759. result->fe_lnlen = &(line[linelen])
  760. - result->fe_lname;
  761. break;
  762. }
  763. p++;
  764. }
  765. }
  766. }
  767. }
  768. result->fe_time.tm_month = atoi(tokens[0]+0);
  769. if (result->fe_time.tm_month != 0)
  770. {
  771. result->fe_time.tm_month--;
  772. result->fe_time.tm_mday = atoi(tokens[0]+3);
  773. result->fe_time.tm_year = atoi(tokens[0]+6);
  774. /* if year has only two digits then assume that
  775. 00-79 is 2000-2079
  776. 80-99 is 1980-1999 */
  777. if (result->fe_time.tm_year < 80)
  778. result->fe_time.tm_year += 2000;
  779. else if (result->fe_time.tm_year < 100)
  780. result->fe_time.tm_year += 1900;
  781. }
  782. result->fe_time.tm_hour = atoi(tokens[1]+0);
  783. result->fe_time.tm_min = atoi(tokens[1]+3);
  784. if (toklen[1] == 7)
  785. {
  786. if ((tokens[1][5]) == 'P' && result->fe_time.tm_hour < 12)
  787. result->fe_time.tm_hour += 12;
  788. else if ((tokens[1][5]) == 'A' && result->fe_time.tm_hour == 12)
  789. result->fe_time.tm_hour = 0;
  790. }
  791. /* the caller should do this (if dropping "." and ".." is desired)
  792. if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
  793. (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
  794. result->fe_fname[1] == '.')))
  795. return '?';
  796. */
  797. return result->fe_type;
  798. } /* if (lstyle == 'W' && (!state->lstyle || state->lstyle == lstyle)) */
  799. } /* if (!lstyle && (!state->lstyle || state->lstyle == 'W')) */
  800. #endif
  801. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  802. #if defined(SUPPORT_OS2)
  803. if (!lstyle && (!state->lstyle || state->lstyle == 'O')) /* OS/2 test */
  804. {
  805. /* 220 server IBM TCP/IP for OS/2 - FTP Server ver 23:04:36 on Jan 15 1997 ready.
  806. * fixed position, space padded columns. I have only a vague idea
  807. * of what the contents between col 18 and 34 might be: All I can infer
  808. * is that there may be attribute flags in there and there may be
  809. * a " DIR" in there.
  810. *
  811. * 1 2 3 4 5 6
  812. *0123456789012345678901234567890123456789012345678901234567890123456789
  813. *----- size -------|??????????????? MM-DD-YY| HH:MM| nnnnnnnnn....
  814. * 0 DIR 04-11-95 16:26 .
  815. * 0 DIR 04-11-95 16:26 ..
  816. * 0 DIR 04-11-95 16:26 ADDRESS
  817. * 612 RHSA 07-28-95 16:45 air_tra1.bag
  818. * 195 A 08-09-95 10:23 Alfa1.bag
  819. * 0 RHS DIR 04-11-95 16:26 ATTACH
  820. * 372 A 08-09-95 10:26 Aussie_1.bag
  821. * 310992 06-28-94 09:56 INSTALL.EXE
  822. * 1 2 3 4
  823. * 01234567890123456789012345678901234567890123456789
  824. * dirlist from the mirror.pl project, col positions from Mozilla.
  825. */
  826. p = &(line[toklen[0]]);
  827. /* \s(\d\d-\d\d-\d\d)\s+(\d\d:\d\d)\s */
  828. if (numtoks >= 4 && toklen[0] <= 18 && isdigit(*tokens[0]) &&
  829. (linelen - toklen[0]) >= (53-18) &&
  830. p[18-18] == ' ' && p[34-18] == ' ' &&
  831. p[37-18] == '-' && p[40-18] == '-' && p[43-18] == ' ' &&
  832. p[45-18] == ' ' && p[48-18] == ':' && p[51-18] == ' ' &&
  833. isdigit(p[35-18]) && isdigit(p[36-18]) &&
  834. isdigit(p[38-18]) && isdigit(p[39-18]) &&
  835. isdigit(p[41-18]) && isdigit(p[42-18]) &&
  836. isdigit(p[46-18]) && isdigit(p[47-18]) &&
  837. isdigit(p[49-18]) && isdigit(p[50-18])
  838. )
  839. {
  840. lstyle = 'O'; /* OS/2 */
  841. if (!state->lstyle)
  842. {
  843. for (pos = 1; lstyle && pos < toklen[0]; pos++)
  844. {
  845. if (!isdigit(tokens[0][pos]))
  846. lstyle = 0;
  847. }
  848. }
  849. }
  850. if (lstyle == 'O')
  851. {
  852. state->parsed_one = 1;
  853. state->lstyle = lstyle;
  854. p = &(line[toklen[0]]);
  855. result->fe_cinfs = 1;
  856. result->fe_fname = &p[53-18];
  857. result->fe_fnlen = (&(line[linelen_sans_wsp]))
  858. - (result->fe_fname);
  859. result->fe_type = 'f';
  860. /* I don't have a real listing to determine exact pos, so scan. */
  861. for (pos = (18-18); pos < ((35-18)-4); pos++)
  862. {
  863. if (p[pos+0] == ' ' && p[pos+1] == 'D' &&
  864. p[pos+2] == 'I' && p[pos+3] == 'R')
  865. {
  866. result->fe_type = 'd';
  867. break;
  868. }
  869. }
  870. if (result->fe_type != 'd')
  871. {
  872. pos = toklen[0];
  873. if (pos > (sizeof(result->fe_size)-1))
  874. pos = (sizeof(result->fe_size)-1);
  875. memcpy( result->fe_size, tokens[0], pos );
  876. result->fe_size[pos] = '\0';
  877. }
  878. result->fe_time.tm_month = atoi(&p[35-18]) - 1;
  879. result->fe_time.tm_mday = atoi(&p[38-18]);
  880. result->fe_time.tm_year = atoi(&p[41-18]);
  881. if (result->fe_time.tm_year < 80)
  882. result->fe_time.tm_year += 100;
  883. result->fe_time.tm_hour = atoi(&p[46-18]);
  884. result->fe_time.tm_min = atoi(&p[49-18]);
  885. /* the caller should do this (if dropping "." and ".." is desired)
  886. if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
  887. (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
  888. result->fe_fname[1] == '.')))
  889. return '?';
  890. */
  891. return result->fe_type;
  892. } /* if (lstyle == 'O') */
  893. } /* if (!lstyle && (!state->lstyle || state->lstyle == 'O')) */
  894. #endif
  895. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  896. #if defined(SUPPORT_LSL)
  897. if (!lstyle && (!state->lstyle || state->lstyle == 'U')) /* /bin/ls & co. */
  898. {
  899. /* UNIX-style listing, without inum and without blocks
  900. * "-rw-r--r-- 1 root other 531 Jan 29 03:26 README"
  901. * "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc"
  902. * "dr-xr-xr-x 2 root 512 Apr 8 1994 etc"
  903. * "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin"
  904. * Also produced by Microsoft's FTP servers for Windows:
  905. * "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z"
  906. * "d--------- 1 owner group 0 May 9 19:45 Softlib"
  907. * Also WFTPD for MSDOS:
  908. * "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp"
  909. * Hellsoft for NetWare:
  910. * "d[RWCEMFA] supervisor 512 Jan 16 18:53 login"
  911. * "-[RWCEMFA] rhesus 214059 Oct 20 15:27 cx.exe"
  912. * Newer Hellsoft for NetWare: (netlab2.usu.edu)
  913. * - [RWCEAFMS] NFAUUser 192 Apr 27 15:21 HEADER.html
  914. * d [RWCEAFMS] jrd 512 Jul 11 03:01 allupdates
  915. * Also NetPresenz for the Mac:
  916. * "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit"
  917. * "drwxrwxr-x folder 2 May 10 1996 network"
  918. * Protected directory:
  919. * "drwx-wx-wt 2 root wheel 512 Jul 1 02:15 incoming"
  920. * uid/gid instead of username/groupname:
  921. * "drwxr-xr-x 2 0 0 512 May 28 22:17 etc"
  922. */
  923. bool is_old_Hellsoft = false;
  924. if (numtoks >= 6)
  925. {
  926. /* there are two perm formats (Hellsoft/NetWare and *IX strmode(3)).
  927. * Scan for size column only if the perm format is one or the other.
  928. */
  929. if (toklen[0] == 1 || (tokens[0][1]) == '[')
  930. {
  931. if (*tokens[0] == 'd' || *tokens[0] == '-')
  932. {
  933. pos = toklen[0]-1;
  934. p = tokens[0] + 1;
  935. if (pos == 0)
  936. {
  937. p = tokens[1];
  938. pos = toklen[1];
  939. }
  940. if ((pos == 9 || pos == 10) &&
  941. (*p == '[' && p[pos-1] == ']') &&
  942. (p[1] == 'R' || p[1] == '-') &&
  943. (p[2] == 'W' || p[2] == '-') &&
  944. (p[3] == 'C' || p[3] == '-') &&
  945. (p[4] == 'E' || p[4] == '-'))
  946. {
  947. /* rest is FMA[S] or AFM[S] */
  948. lstyle = 'U'; /* very likely one of the NetWare servers */
  949. if (toklen[0] == 10)
  950. is_old_Hellsoft = true;
  951. }
  952. }
  953. }
  954. else if ((toklen[0] == 10 || toklen[0] == 11)
  955. && strchr("-bcdlpsw?DFam", *tokens[0]))
  956. {
  957. p = &(tokens[0][1]);
  958. if ((p[0] == 'r' || p[0] == '-') &&
  959. (p[1] == 'w' || p[1] == '-') &&
  960. (p[3] == 'r' || p[3] == '-') &&
  961. (p[4] == 'w' || p[4] == '-') &&
  962. (p[6] == 'r' || p[6] == '-') &&
  963. (p[7] == 'w' || p[7] == '-'))
  964. /* 'x'/p[9] can be S|s|x|-|T|t or implementation specific */
  965. {
  966. lstyle = 'U'; /* very likely /bin/ls */
  967. }
  968. }
  969. }
  970. if (lstyle == 'U') /* first token checks out */
  971. {
  972. lstyle = 0;
  973. for (pos = (numtoks-5); !lstyle && pos > 1; pos--)
  974. {
  975. /* scan for: (\d+)\s+([A-Z][a-z][a-z])\s+
  976. * (\d\d\d\d|\d\:\d\d|\d\d\:\d\d|\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d)
  977. * \s+(.+)$
  978. */
  979. if (isdigit(*tokens[pos]) /* size */
  980. /* (\w\w\w) */
  981. && toklen[pos+1] == 3 && isalpha(*tokens[pos+1]) &&
  982. isalpha(tokens[pos+1][1]) && isalpha(tokens[pos+1][2])
  983. /* (\d|\d\d) */
  984. && isdigit(*tokens[pos+2]) &&
  985. (toklen[pos+2] == 1 ||
  986. (toklen[pos+2] == 2 && isdigit(tokens[pos+2][1])))
  987. && toklen[pos+3] >= 4 && isdigit(*tokens[pos+3])
  988. /* (\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d) */
  989. && (toklen[pos+3] <= 5 || (
  990. (toklen[pos+3] == 7 || toklen[pos+3] == 8) &&
  991. (tokens[pos+3][toklen[pos+3]-3]) == ':'))
  992. && isdigit(tokens[pos+3][toklen[pos+3]-2])
  993. && isdigit(tokens[pos+3][toklen[pos+3]-1])
  994. && (
  995. /* (\d\d\d\d) */
  996. ((toklen[pos+3] == 4 || toklen[pos+3] == 5) &&
  997. isdigit(tokens[pos+3][1]) &&
  998. isdigit(tokens[pos+3][2]) )
  999. /* (\d\:\d\d|\d\:\d\d\:\d\d) */
  1000. || ((toklen[pos+3] == 4 || toklen[pos+3] == 7) &&
  1001. (tokens[pos+3][1]) == ':' &&
  1002. isdigit(tokens[pos+3][2]) && isdigit(tokens[pos+3][3]))
  1003. /* (\d\d\:\d\d|\d\d\:\d\d\:\d\d) */
  1004. || ((toklen[pos+3] == 5 || toklen[pos+3] == 8) &&
  1005. isdigit(tokens[pos+3][1]) && (tokens[pos+3][2]) == ':' &&
  1006. isdigit(tokens[pos+3][3]) && isdigit(tokens[pos+3][4]))
  1007. )
  1008. )
  1009. {
  1010. lstyle = 'U'; /* assume /bin/ls or variant format */
  1011. tokmarker = pos;
  1012. /* check that size is numeric */
  1013. p = tokens[tokmarker];
  1014. unsigned int i;
  1015. for (i = 0; i < toklen[tokmarker]; i++)
  1016. {
  1017. if (!isdigit(*p++))
  1018. {
  1019. lstyle = 0;
  1020. break;
  1021. }
  1022. }
  1023. if (lstyle)
  1024. {
  1025. month_num = 0;
  1026. p = tokens[tokmarker+1];
  1027. for (i = 0; i < (12*3); i+=3)
  1028. {
  1029. if (p[0] == month_names[i+0] &&
  1030. p[1] == month_names[i+1] &&
  1031. p[2] == month_names[i+2])
  1032. break;
  1033. month_num++;
  1034. }
  1035. if (month_num >= 12)
  1036. lstyle = 0;
  1037. }
  1038. } /* relative position test */
  1039. } /* for (pos = (numtoks-5); !lstyle && pos > 1; pos--) */
  1040. } /* if (lstyle == 'U') */
  1041. if (lstyle == 'U')
  1042. {
  1043. state->parsed_one = 1;
  1044. state->lstyle = lstyle;
  1045. result->fe_cinfs = 0;
  1046. result->fe_type = '?';
  1047. if (*tokens[0] == 'd' || *tokens[0] == 'l')
  1048. result->fe_type = *tokens[0];
  1049. else if (*tokens[0] == 'D')
  1050. result->fe_type = 'd';
  1051. else if (*tokens[0] == '-' || *tokens[0] == 'F')
  1052. result->fe_type = 'f'; /* (hopefully a regular file) */
  1053. if (result->fe_type != 'd')
  1054. {
  1055. pos = toklen[tokmarker];
  1056. if (pos > (sizeof(result->fe_size)-1))
  1057. pos = (sizeof(result->fe_size)-1);
  1058. memcpy( result->fe_size, tokens[tokmarker], pos );
  1059. result->fe_size[pos] = '\0';
  1060. }
  1061. result->fe_time.tm_month = month_num;
  1062. result->fe_time.tm_mday = atoi(tokens[tokmarker+2]);
  1063. if (result->fe_time.tm_mday == 0)
  1064. result->fe_time.tm_mday++;
  1065. p = tokens[tokmarker+3];
  1066. pos = (unsigned int)atoi(p);
  1067. if (p[1] == ':') /* one digit hour */
  1068. p--;
  1069. if (p[2] != ':') /* year */
  1070. {
  1071. result->fe_time.tm_year = pos;
  1072. }
  1073. else
  1074. {
  1075. result->fe_time.tm_hour = pos;
  1076. result->fe_time.tm_min = atoi(p+3);
  1077. if (p[5] == ':')
  1078. result->fe_time.tm_sec = atoi(p+6);
  1079. if (!state->now_time)
  1080. {
  1081. state->now_time = PR_Now();
  1082. PR_ExplodeTime((state->now_time), PR_LocalTimeParameters, &(state->now_tm) );
  1083. }
  1084. result->fe_time.tm_year = state->now_tm.tm_year;
  1085. if ( (( state->now_tm.tm_month << 5) + state->now_tm.tm_mday) <
  1086. ((result->fe_time.tm_month << 5) + result->fe_time.tm_mday) )
  1087. result->fe_time.tm_year--;
  1088. } /* time/year */
  1089. // The length of the whole date string should be 12. On AIX the length
  1090. // is only 11 when the year is present in the date string and there is
  1091. // 1 padding space at the end of the string. In both cases the filename
  1092. // starts at offset 13 from the start of the date string.
  1093. // Don't care about leading spaces when the date string has different
  1094. // format or when old Hellsoft output was detected.
  1095. {
  1096. const char *date_start = tokens[tokmarker+1];
  1097. const char *date_end = tokens[tokmarker+3] + toklen[tokmarker+3];
  1098. if (!is_old_Hellsoft && ((date_end - date_start) == 12 ||
  1099. ((date_end - date_start) == 11 && date_end[1] == ' ')))
  1100. result->fe_fname = date_start + 13;
  1101. else
  1102. result->fe_fname = tokens[tokmarker+4];
  1103. }
  1104. result->fe_fnlen = (&(line[linelen]))
  1105. - (result->fe_fname);
  1106. if (result->fe_type == 'l' && result->fe_fnlen > 4)
  1107. {
  1108. /* First try to use result->fe_size to find " -> " sequence.
  1109. This can give proper result for cases like "aaa -> bbb -> ccc". */
  1110. uint32_t fe_size = atoi(result->fe_size);
  1111. if (result->fe_fnlen > (fe_size + 4) &&
  1112. PL_strncmp(result->fe_fname + result->fe_fnlen - fe_size - 4 , " -> ", 4) == 0)
  1113. {
  1114. result->fe_lname = result->fe_fname + (result->fe_fnlen - fe_size);
  1115. result->fe_lnlen = (&(line[linelen])) - (result->fe_lname);
  1116. result->fe_fnlen -= fe_size + 4;
  1117. }
  1118. else
  1119. {
  1120. /* Search for sequence " -> " from the end for case when there are
  1121. more occurrences. F.e. if ftpd returns "a -> b -> c" assume
  1122. "a -> b" as a name. Powerusers can remove unnecessary parts
  1123. manually but there is no way to follow the link when some
  1124. essential part is missing. */
  1125. p = result->fe_fname + (result->fe_fnlen - 5);
  1126. for (pos = (result->fe_fnlen - 5); pos > 0; pos--)
  1127. {
  1128. if (PL_strncmp(p, " -> ", 4) == 0)
  1129. {
  1130. result->fe_lname = p + 4;
  1131. result->fe_lnlen = (&(line[linelen]))
  1132. - (result->fe_lname);
  1133. result->fe_fnlen = pos;
  1134. break;
  1135. }
  1136. p--;
  1137. }
  1138. }
  1139. }
  1140. #if defined(SUPPORT_LSLF) /* some (very rare) servers return ls -lF */
  1141. if (result->fe_fnlen > 1)
  1142. {
  1143. p = result->fe_fname[result->fe_fnlen-1];
  1144. pos = result->fe_type;
  1145. if (pos == 'd') {
  1146. if (*p == '/') result->fe_fnlen--; /* directory */
  1147. } else if (pos == 'l') {
  1148. if (*p == '@') result->fe_fnlen--; /* symlink */
  1149. } else if (pos == 'f') {
  1150. if (*p == '*') result->fe_fnlen--; /* executable */
  1151. } else if (*p == '=' || *p == '%' || *p == '|') {
  1152. result->fe_fnlen--; /* socket, whiteout, fifo */
  1153. }
  1154. }
  1155. #endif
  1156. /* the caller should do this (if dropping "." and ".." is desired)
  1157. if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
  1158. (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
  1159. result->fe_fname[1] == '.')))
  1160. return '?';
  1161. */
  1162. return result->fe_type;
  1163. } /* if (lstyle == 'U') */
  1164. } /* if (!lstyle && (!state->lstyle || state->lstyle == 'U')) */
  1165. #endif
  1166. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  1167. #if defined(SUPPORT_W16) /* 16bit Windows */
  1168. if (!lstyle && (!state->lstyle || state->lstyle == 'w'))
  1169. { /* old SuperTCP suite FTP server for Win3.1 */
  1170. /* old NetManage Chameleon TCP/IP suite FTP server for Win3.1 */
  1171. /*
  1172. * SuperTCP dirlist from the mirror.pl project
  1173. * mon/day/year separator may be '/' or '-'.
  1174. * . <DIR> 11-16-94 17:16
  1175. * .. <DIR> 11-16-94 17:16
  1176. * INSTALL <DIR> 11-16-94 17:17
  1177. * CMT <DIR> 11-21-94 10:17
  1178. * DESIGN1.DOC 11264 05-11-95 14:20
  1179. * README.TXT 1045 05-10-95 11:01
  1180. * WPKIT1.EXE 960338 06-21-95 17:01
  1181. * CMT.CSV 0 07-06-95 14:56
  1182. *
  1183. * Chameleon dirlist guessed from lynx
  1184. * . <DIR> Nov 16 1994 17:16
  1185. * .. <DIR> Nov 16 1994 17:16
  1186. * INSTALL <DIR> Nov 16 1994 17:17
  1187. * CMT <DIR> Nov 21 1994 10:17
  1188. * DESIGN1.DOC 11264 May 11 1995 14:20 A
  1189. * README.TXT 1045 May 10 1995 11:01
  1190. * WPKIT1.EXE 960338 Jun 21 1995 17:01 R
  1191. * CMT.CSV 0 Jul 06 1995 14:56 RHA
  1192. */
  1193. if (numtoks >= 4 && toklen[0] < 13 &&
  1194. ((toklen[1] == 5 && *tokens[1] == '<') || isdigit(*tokens[1])) )
  1195. {
  1196. if (numtoks == 4
  1197. && (toklen[2] == 8 || toklen[2] == 9)
  1198. && (((tokens[2][2]) == '/' && (tokens[2][5]) == '/') ||
  1199. ((tokens[2][2]) == '-' && (tokens[2][5]) == '-'))
  1200. && (toklen[3] == 4 || toklen[3] == 5)
  1201. && (tokens[3][toklen[3]-3]) == ':'
  1202. && isdigit(tokens[2][0]) && isdigit(tokens[2][1])
  1203. && isdigit(tokens[2][3]) && isdigit(tokens[2][4])
  1204. && isdigit(tokens[2][6]) && isdigit(tokens[2][7])
  1205. && (toklen[2] < 9 || isdigit(tokens[2][8]))
  1206. && isdigit(tokens[3][toklen[3]-1]) && isdigit(tokens[3][toklen[3]-2])
  1207. && isdigit(tokens[3][toklen[3]-4]) && isdigit(*tokens[3])
  1208. )
  1209. {
  1210. lstyle = 'w';
  1211. }
  1212. else if ((numtoks == 6 || numtoks == 7)
  1213. && toklen[2] == 3 && toklen[3] == 2
  1214. && toklen[4] == 4 && toklen[5] == 5
  1215. && (tokens[5][2]) == ':'
  1216. && isalpha(tokens[2][0]) && isalpha(tokens[2][1])
  1217. && isalpha(tokens[2][2])
  1218. && isdigit(tokens[3][0]) && isdigit(tokens[3][1])
  1219. && isdigit(tokens[4][0]) && isdigit(tokens[4][1])
  1220. && isdigit(tokens[4][2]) && isdigit(tokens[4][3])
  1221. && isdigit(tokens[5][0]) && isdigit(tokens[5][1])
  1222. && isdigit(tokens[5][3]) && isdigit(tokens[5][4])
  1223. /* could also check that (&(tokens[5][5]) - tokens[2]) == 17 */
  1224. )
  1225. {
  1226. lstyle = 'w';
  1227. }
  1228. if (lstyle && state->lstyle != lstyle) /* first time */
  1229. {
  1230. p = tokens[1];
  1231. if (toklen[1] != 5 || p[0] != '<' || p[1] != 'D' ||
  1232. p[2] != 'I' || p[3] != 'R' || p[4] != '>')
  1233. {
  1234. for (pos = 0; lstyle && pos < toklen[1]; pos++)
  1235. {
  1236. if (!isdigit(*p++))
  1237. lstyle = 0;
  1238. }
  1239. } /* not <DIR> */
  1240. } /* if (first time) */
  1241. } /* if (numtoks == ...) */
  1242. if (lstyle == 'w')
  1243. {
  1244. state->parsed_one = 1;
  1245. state->lstyle = lstyle;
  1246. result->fe_cinfs = 1;
  1247. result->fe_fname = tokens[0];
  1248. result->fe_fnlen = toklen[0];
  1249. result->fe_type = 'd';
  1250. p = tokens[1];
  1251. if (isdigit(*p))
  1252. {
  1253. result->fe_type = 'f';
  1254. pos = toklen[1];
  1255. if (pos > (sizeof(result->fe_size)-1))
  1256. pos = sizeof(result->fe_size)-1;
  1257. memcpy( result->fe_size, p, pos );
  1258. result->fe_size[pos] = '\0';
  1259. }
  1260. p = tokens[2];
  1261. if (toklen[2] == 3) /* Chameleon */
  1262. {
  1263. tbuf[0] = toupper(p[0]);
  1264. tbuf[1] = tolower(p[1]);
  1265. tbuf[2] = tolower(p[2]);
  1266. for (pos = 0; pos < (12*3); pos+=3)
  1267. {
  1268. if (tbuf[0] == month_names[pos+0] &&
  1269. tbuf[1] == month_names[pos+1] &&
  1270. tbuf[2] == month_names[pos+2])
  1271. {
  1272. result->fe_time.tm_month = pos/3;
  1273. result->fe_time.tm_mday = atoi(tokens[3]);
  1274. result->fe_time.tm_year = atoi(tokens[4]) - 1900;
  1275. break;
  1276. }
  1277. }
  1278. pos = 5; /* Chameleon toknum of date field */
  1279. }
  1280. else
  1281. {
  1282. result->fe_time.tm_month = atoi(p+0)-1;
  1283. result->fe_time.tm_mday = atoi(p+3);
  1284. result->fe_time.tm_year = atoi(p+6);
  1285. if (result->fe_time.tm_year < 80) /* SuperTCP */
  1286. result->fe_time.tm_year += 100;
  1287. pos = 3; /* SuperTCP toknum of date field */
  1288. }
  1289. result->fe_time.tm_hour = atoi(tokens[pos]);
  1290. result->fe_time.tm_min = atoi(&(tokens[pos][toklen[pos]-2]));
  1291. /* the caller should do this (if dropping "." and ".." is desired)
  1292. if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
  1293. (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
  1294. result->fe_fname[1] == '.')))
  1295. return '?';
  1296. */
  1297. return result->fe_type;
  1298. } /* (lstyle == 'w') */
  1299. } /* if (!lstyle && (!state->lstyle || state->lstyle == 'w')) */
  1300. #endif
  1301. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  1302. #if defined(SUPPORT_DLS) /* dls -dtR */
  1303. if (!lstyle &&
  1304. (state->lstyle == 'D' || (!state->lstyle && state->numlines == 1)))
  1305. /* /bin/dls lines have to be immediately recognizable (first line) */
  1306. {
  1307. /* I haven't seen an FTP server that delivers a /bin/dls listing,
  1308. * but can infer the format from the lynx and mirror.pl projects.
  1309. * Both formats are supported.
  1310. *
  1311. * Lynx says:
  1312. * README 763 Information about this server\0
  1313. * bin/ - \0
  1314. * etc/ = \0
  1315. * ls-lR 0 \0
  1316. * ls-lR.Z 3 \0
  1317. * pub/ = Public area\0
  1318. * usr/ - \0
  1319. * morgan 14 -> ../real/morgan\0
  1320. * TIMIT.mostlikely.Z\0
  1321. * 79215 \0
  1322. *
  1323. * mirror.pl says:
  1324. * filename: ^(\S*)\s+
  1325. * size: (\-|\=|\d+)\s+
  1326. * month/day: ((\w\w\w\s+\d+|\d+\s+\w\w\w)\s+
  1327. * time/year: (\d+:\d+|\d\d\d\d))\s+
  1328. * rest: (.+)
  1329. *
  1330. * README 763 Jul 11 21:05 Information about this server
  1331. * bin/ - Apr 28 1994
  1332. * etc/ = 11 Jul 21:04
  1333. * ls-lR 0 6 Aug 17:14
  1334. * ls-lR.Z 3 05 Sep 1994
  1335. * pub/ = Jul 11 21:04 Public area
  1336. * usr/ - Sep 7 09:39
  1337. * morgan 14 Apr 18 09:39 -> ../real/morgan
  1338. * TIMIT.mostlikely.Z
  1339. * 79215 Jul 11 21:04
  1340. */
  1341. if (!state->lstyle && line[linelen-1] == ':' &&
  1342. linelen >= 2 && toklen[numtoks-1] != 1)
  1343. {
  1344. /* code in mirror.pl suggests that a listing may be preceded
  1345. * by a PWD line in the form "/some/dir/names/here:"
  1346. * but does not necessarily begin with '/'. *sigh*
  1347. */
  1348. pos = 0;
  1349. p = line;
  1350. while (pos < (linelen-1))
  1351. {
  1352. /* illegal (or extremely unusual) chars in a dirspec */
  1353. if (*p == '<' || *p == '|' || *p == '>' ||
  1354. *p == '?' || *p == '*' || *p == '\\')
  1355. break;
  1356. if (*p == '/' && pos < (linelen-2) && p[1] == '/')
  1357. break;
  1358. pos++;
  1359. p++;
  1360. }
  1361. if (pos == (linelen-1))
  1362. {
  1363. state->lstyle = 'D';
  1364. return '?';
  1365. }
  1366. }
  1367. if (!lstyle && numtoks >= 2)
  1368. {
  1369. pos = 22; /* pos of (\d+|-|=) if this is not part of a multiline */
  1370. if (state->lstyle && carry_buf_len) /* first is from previous line */
  1371. pos = toklen[1]-1; /* and is 'as-is' (may contain whitespace) */
  1372. if (linelen > pos)
  1373. {
  1374. p = &line[pos];
  1375. if ((*p == '-' || *p == '=' || isdigit(*p)) &&
  1376. ((linelen == (pos+1)) ||
  1377. (linelen >= (pos+3) && p[1] == ' ' && p[2] == ' ')) )
  1378. {
  1379. tokmarker = 1;
  1380. if (!carry_buf_len)
  1381. {
  1382. pos = 1;
  1383. while (pos < numtoks && (tokens[pos]+toklen[pos]) < (&line[23]))
  1384. pos++;
  1385. tokmarker = 0;
  1386. if ((tokens[pos]+toklen[pos]) == (&line[23]))
  1387. tokmarker = pos;
  1388. }
  1389. if (tokmarker)
  1390. {
  1391. lstyle = 'D';
  1392. if (*tokens[tokmarker] == '-' || *tokens[tokmarker] == '=')
  1393. {
  1394. if (toklen[tokmarker] != 1 ||
  1395. (tokens[tokmarker-1][toklen[tokmarker-1]-1]) != '/')
  1396. lstyle = 0;
  1397. }
  1398. else
  1399. {
  1400. for (pos = 0; lstyle && pos < toklen[tokmarker]; pos++)
  1401. {
  1402. if (!isdigit(tokens[tokmarker][pos]))
  1403. lstyle = 0;
  1404. }
  1405. }
  1406. if (lstyle && !state->lstyle) /* first time */
  1407. {
  1408. /* scan for illegal (or incredibly unusual) chars in fname */
  1409. for (p = tokens[0]; lstyle &&
  1410. p < &(tokens[tokmarker-1][toklen[tokmarker-1]]); p++)
  1411. {
  1412. if (*p == '<' || *p == '|' || *p == '>' ||
  1413. *p == '?' || *p == '*' || *p == '/' || *p == '\\')
  1414. lstyle = 0;
  1415. }
  1416. }
  1417. } /* size token found */
  1418. } /* expected chars behind expected size token */
  1419. } /* if (linelen > pos) */
  1420. } /* if (!lstyle && numtoks >= 2) */
  1421. if (!lstyle && state->lstyle == 'D' && !carry_buf_len)
  1422. {
  1423. /* the filename of a multi-line entry can be identified
  1424. * correctly only if dls format had been previously established.
  1425. * This should always be true because there should be entries
  1426. * for '.' and/or '..' and/or CWD that precede the rest of the
  1427. * listing.
  1428. */
  1429. pos = linelen;
  1430. if (pos > (sizeof(state->carry_buf)-1))
  1431. pos = sizeof(state->carry_buf)-1;
  1432. memcpy( state->carry_buf, line, pos );
  1433. state->carry_buf_len = pos;
  1434. return '?';
  1435. }
  1436. if (lstyle == 'D')
  1437. {
  1438. state->parsed_one = 1;
  1439. state->lstyle = lstyle;
  1440. p = &(tokens[tokmarker-1][toklen[tokmarker-1]]);
  1441. result->fe_fname = tokens[0];
  1442. result->fe_fnlen = p - tokens[0];
  1443. result->fe_type = 'f';
  1444. if (result->fe_fname[result->fe_fnlen-1] == '/')
  1445. {
  1446. if (result->fe_lnlen == 1)
  1447. result->fe_type = '?';
  1448. else
  1449. {
  1450. result->fe_fnlen--;
  1451. result->fe_type = 'd';
  1452. }
  1453. }
  1454. else if (isdigit(*tokens[tokmarker]))
  1455. {
  1456. pos = toklen[tokmarker];
  1457. if (pos > (sizeof(result->fe_size)-1))
  1458. pos = sizeof(result->fe_size)-1;
  1459. memcpy( result->fe_size, tokens[tokmarker], pos );
  1460. result->fe_size[pos] = '\0';
  1461. }
  1462. if ((tokmarker+3) < numtoks &&
  1463. (&(tokens[numtoks-1][toklen[numtoks-1]]) -
  1464. tokens[tokmarker+1]) >= (1+1+3+1+4) )
  1465. {
  1466. pos = (tokmarker+3);
  1467. p = tokens[pos];
  1468. pos = toklen[pos];
  1469. if ((pos == 4 || pos == 5)
  1470. && isdigit(*p) && isdigit(p[pos-1]) && isdigit(p[pos-2])
  1471. && ((pos == 5 && p[2] == ':') ||
  1472. (pos == 4 && (isdigit(p[1]) || p[1] == ':')))
  1473. )
  1474. {
  1475. month_num = tokmarker+1; /* assumed position of month field */
  1476. pos = tokmarker+2; /* assumed position of mday field */
  1477. if (isdigit(*tokens[month_num])) /* positions are reversed */
  1478. {
  1479. month_num++;
  1480. pos--;
  1481. }
  1482. p = tokens[month_num];
  1483. if (isdigit(*tokens[pos])
  1484. && (toklen[pos] == 1 ||
  1485. (toklen[pos] == 2 && isdigit(tokens[pos][1])))
  1486. && toklen[month_num] == 3
  1487. && isalpha(*p) && isalpha(p[1]) && isalpha(p[2]) )
  1488. {
  1489. pos = atoi(tokens[pos]);
  1490. if (pos > 0 && pos <= 31)
  1491. {
  1492. result->fe_time.tm_mday = pos;
  1493. month_num = 1;
  1494. for (pos = 0; pos < (12*3); pos+=3)
  1495. {
  1496. if (p[0] == month_names[pos+0] &&
  1497. p[1] == month_names[pos+1] &&
  1498. p[2] == month_names[pos+2])
  1499. break;
  1500. month_num++;
  1501. }
  1502. if (month_num > 12)
  1503. result->fe_time.tm_mday = 0;
  1504. else
  1505. result->fe_time.tm_month = month_num - 1;
  1506. }
  1507. }
  1508. if (result->fe_time.tm_mday)
  1509. {
  1510. tokmarker += 3; /* skip mday/mon/yrtime (to find " -> ") */
  1511. p = tokens[tokmarker];
  1512. pos = atoi(p);
  1513. if (pos > 24)
  1514. result->fe_time.tm_year = pos-1900;
  1515. else
  1516. {
  1517. if (p[1] == ':')
  1518. p--;
  1519. result->fe_time.tm_hour = pos;
  1520. result->fe_time.tm_min = atoi(p+3);
  1521. if (!state->now_time)
  1522. {
  1523. state->now_time = PR_Now();
  1524. PR_ExplodeTime((state->now_time), PR_LocalTimeParameters, &(state->now_tm) );
  1525. }
  1526. result->fe_time.tm_year = state->now_tm.tm_year;
  1527. if ( (( state->now_tm.tm_month << 4) + state->now_tm.tm_mday) <
  1528. ((result->fe_time.tm_month << 4) + result->fe_time.tm_mday) )
  1529. result->fe_time.tm_year--;
  1530. } /* got year or time */
  1531. } /* got month/mday */
  1532. } /* may have year or time */
  1533. } /* enough remaining to possibly have date/time */
  1534. if (numtoks > (tokmarker+2))
  1535. {
  1536. pos = tokmarker+1;
  1537. p = tokens[pos];
  1538. if (toklen[pos] == 2 && *p == '-' && p[1] == '>')
  1539. {
  1540. p = &(tokens[numtoks-1][toklen[numtoks-1]]);
  1541. result->fe_type = 'l';
  1542. result->fe_lname = tokens[pos+1];
  1543. result->fe_lnlen = p - result->fe_lname;
  1544. if (result->fe_lnlen > 1 &&
  1545. result->fe_lname[result->fe_lnlen-1] == '/')
  1546. result->fe_lnlen--;
  1547. }
  1548. } /* if (numtoks > (tokmarker+2)) */
  1549. /* the caller should do this (if dropping "." and ".." is desired)
  1550. if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
  1551. (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
  1552. result->fe_fname[1] == '.')))
  1553. return '?';
  1554. */
  1555. return result->fe_type;
  1556. } /* if (lstyle == 'D') */
  1557. } /* if (!lstyle && (!state->lstyle || state->lstyle == 'D')) */
  1558. #endif
  1559. /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
  1560. } /* if (linelen > 0) */
  1561. return ParsingFailed(state);
  1562. }
  1563. /* ==================================================================== */
  1564. /* standalone testing */
  1565. /* ==================================================================== */
  1566. #if 0
  1567. #include <stdio.h>
  1568. static int do_it(FILE *outfile,
  1569. char *line, size_t linelen, struct list_state *state,
  1570. char **cmnt_buf, unsigned int *cmnt_buf_sz,
  1571. char **list_buf, unsigned int *list_buf_sz )
  1572. {
  1573. struct list_result result;
  1574. char *p;
  1575. int rc;
  1576. rc = ParseFTPList( line, state, &result );
  1577. if (!outfile)
  1578. {
  1579. outfile = stdout;
  1580. if (rc == '?')
  1581. fprintf(outfile, "junk: %.*s\n", (int)linelen, line );
  1582. else if (rc == '"')
  1583. fprintf(outfile, "cmnt: %.*s\n", (int)linelen, line );
  1584. else
  1585. fprintf(outfile,
  1586. "list: %02u-%02u-%02u %02u:%02u%cM %20s %.*s%s%.*s\n",
  1587. (result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0),
  1588. result.fe_time.tm_mday,
  1589. (result.fe_time.tm_mday ? (result.fe_time.tm_year % 100) : 0),
  1590. result.fe_time.tm_hour -
  1591. ((result.fe_time.tm_hour > 12)?(12):(0)),
  1592. result.fe_time.tm_min,
  1593. ((result.fe_time.tm_hour >= 12) ? 'P' : 'A'),
  1594. (rc == 'd' ? "<DIR> " :
  1595. (rc == 'l' ? "<JUNCTION> " : result.fe_size)),
  1596. (int)result.fe_fnlen, result.fe_fname,
  1597. ((rc == 'l' && result.fe_lnlen) ? " -> " : ""),
  1598. (int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0),
  1599. ((rc == 'l' && result.fe_lnlen) ? result.fe_lname : "") );
  1600. }
  1601. else if (rc != '?') /* NOT junk */
  1602. {
  1603. char **bufp = list_buf;
  1604. unsigned int *bufz = list_buf_sz;
  1605. if (rc == '"') /* comment - make it a 'result' */
  1606. {
  1607. memset( &result, 0, sizeof(result));
  1608. result.fe_fname = line;
  1609. result.fe_fnlen = linelen;
  1610. result.fe_type = 'f';
  1611. if (line[linelen-1] == '/')
  1612. {
  1613. result.fe_type = 'd';
  1614. result.fe_fnlen--;
  1615. }
  1616. bufp = cmnt_buf;
  1617. bufz = cmnt_buf_sz;
  1618. rc = result.fe_type;
  1619. }
  1620. linelen = 80 + result.fe_fnlen + result.fe_lnlen;
  1621. p = (char *)realloc( *bufp, *bufz + linelen );
  1622. if (!p)
  1623. return -1;
  1624. sprintf( &p[*bufz],
  1625. "%02u-%02u-%04u %02u:%02u:%02u %20s %.*s%s%.*s\n",
  1626. (result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0),
  1627. result.fe_time.tm_mday,
  1628. (result.fe_time.tm_mday ? (result.fe_time.tm_year + 1900) : 0),
  1629. result.fe_time.tm_hour,
  1630. result.fe_time.tm_min,
  1631. result.fe_time.tm_sec,
  1632. (rc == 'd' ? "<DIR> " :
  1633. (rc == 'l' ? "<JUNCTION> " : result.fe_size)),
  1634. (int)result.fe_fnlen, result.fe_fname,
  1635. ((rc == 'l' && result.fe_lnlen) ? " -> " : ""),
  1636. (int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0),
  1637. ((rc == 'l' && result.fe_lnlen) ? result.fe_lname : "") );
  1638. linelen = strlen(&p[*bufz]);
  1639. *bufp = p;
  1640. *bufz = *bufz + linelen;
  1641. }
  1642. return 0;
  1643. }
  1644. int main(int argc, char *argv[])
  1645. {
  1646. FILE *infile = (FILE *)0;
  1647. FILE *outfile = (FILE *)0;
  1648. int need_close_in = 0;
  1649. int need_close_out = 0;
  1650. if (argc > 1)
  1651. {
  1652. infile = stdin;
  1653. if (strcmp(argv[1], "-") == 0)
  1654. need_close_in = 0;
  1655. else if ((infile = fopen(argv[1], "r")) != ((FILE *)0))
  1656. need_close_in = 1;
  1657. else
  1658. fprintf(stderr, "Unable to open input file '%s'\n", argv[1]);
  1659. }
  1660. if (infile && argc > 2)
  1661. {
  1662. outfile = stdout;
  1663. if (strcmp(argv[2], "-") == 0)
  1664. need_close_out = 0;
  1665. else if ((outfile = fopen(argv[2], "w")) != ((FILE *)0))
  1666. need_close_out = 1;
  1667. else
  1668. {
  1669. fprintf(stderr, "Unable to open output file '%s'\n", argv[2]);
  1670. fclose(infile);
  1671. infile = (FILE *)0;
  1672. }
  1673. }
  1674. if (!infile)
  1675. {
  1676. char *appname = &(argv[0][strlen(argv[0])]);
  1677. while (appname > argv[0])
  1678. {
  1679. appname--;
  1680. if (*appname == '/' || *appname == '\\' || *appname == ':')
  1681. {
  1682. appname++;
  1683. break;
  1684. }
  1685. }
  1686. fprintf(stderr,
  1687. "Usage: %s <inputfilename> [<outputfilename>]\n"
  1688. "\nIf an outout file is specified the results will be"
  1689. "\nbe post-processed, and only the file entries will appear"
  1690. "\n(or all comments if there are no file entries)."
  1691. "\nNot specifying an output file causes %s to run in \"debug\""
  1692. "\nmode, ie results are printed as lines are parsed."
  1693. "\nIf a filename is a single dash ('-'), stdin/stdout is used."
  1694. "\n", appname, appname );
  1695. }
  1696. else
  1697. {
  1698. char *cmnt_buf = (char *)0;
  1699. unsigned int cmnt_buf_sz = 0;
  1700. char *list_buf = (char *)0;
  1701. unsigned int list_buf_sz = 0;
  1702. struct list_state state;
  1703. char line[512];
  1704. memset( &state, 0, sizeof(state) );
  1705. while (fgets(line, sizeof(line), infile))
  1706. {
  1707. size_t linelen = strlen(line);
  1708. if (linelen < (sizeof(line)-1))
  1709. {
  1710. if (linelen > 0 && line[linelen-1] == '\n')
  1711. linelen--;
  1712. if (do_it( outfile, line, linelen, &state,
  1713. &cmnt_buf, &cmnt_buf_sz, &list_buf, &list_buf_sz) != 0)
  1714. {
  1715. fprintf(stderr, "Insufficient memory. Listing may be incomplete.\n");
  1716. break;
  1717. }
  1718. }
  1719. else
  1720. {
  1721. /* no '\n' found. drop this and everything up to the next '\n' */
  1722. fprintf(stderr, "drop: %.*s", (int)linelen, line );
  1723. while (linelen == sizeof(line))
  1724. {
  1725. if (!fgets(line, sizeof(line), infile))
  1726. break;
  1727. linelen = 0;
  1728. while (linelen < sizeof(line) && line[linelen] != '\n')
  1729. linelen++;
  1730. fprintf(stderr, "%.*s", (int)linelen, line );
  1731. }
  1732. fprintf(stderr, "\n");
  1733. }
  1734. }
  1735. if (outfile)
  1736. {
  1737. if (list_buf)
  1738. fwrite( list_buf, 1, list_buf_sz, outfile );
  1739. else if (cmnt_buf)
  1740. fwrite( cmnt_buf, 1, cmnt_buf_sz, outfile );
  1741. }
  1742. if (list_buf)
  1743. free(list_buf);
  1744. if (cmnt_buf)
  1745. free(cmnt_buf);
  1746. if (need_close_in)
  1747. fclose(infile);
  1748. if (outfile && need_close_out)
  1749. fclose(outfile);
  1750. }
  1751. return 0;
  1752. }
  1753. #endif